Changelog¶
All notable changes to LLM-Rosetta are documented here. This project follows Keep a Changelog conventions.
v0.6.7 — 2026-06-04¶
Fixed¶
- Embedding endpoint upstream_model alias: The
/v1/embeddingspassthrough handler now substitutes theupstream_modelname into the request body before forwarding, matching the behavior of the chat completions proxy handler. Previously model aliases (e.g.bge-m3→BAAI/bge-m3) were ignored, causing upstream model-not-found errors. - Admin test timer leak: The elapsed-time counter is now tracked globally and cleared when a new test starts, preventing multiple timers from writing alternating values to the same display element.
- Admin test timeout auto-cancel: When the browser-side 120s timeout fires, the server-side task is now explicitly cancelled via the API instead of being left running.
- Server-side test task timeout: Added
asyncio.wait_for()with a 120s timeout to_run_test_task, so hung upstream calls are terminated server-side instead of lingering until the 300s cleanup window.
v0.6.6 — 2026-06-03¶
Added¶
- Admin status bar total requests: Lifetime request counter shown as the first footer segment with locale-aware thousand separators; per-segment hover tooltips (en/zh) explain each metric
- Vendor httpclient URL-encoded form data:
httpclientv0.4.2 — whendatais a dict without files, encode asapplication/x-www-form-urlencodedinstead of requiring explicit serialization
Changed¶
- Schema sanitization module split: JSON Schema sanitization extracted from
converters/base/tools.pyinto its ownconverters/base/schema.pymodule for clearer separation of concerns - Cyclomatic complexity reduction: Reduced cognitive complexity across tool ops (cross-converter
extract_part_ids/log_orphan_warningsreuse), gateway auth (check_admin_auth), proxy streaming (process_stream_chunk), config parsing, logging, and admin routes - complexipy threshold: Raised
max-complexity-allowedfrom 15 to 25; addedcomplexipy-pre-commithook definition (commented out) for future enablement
Fixed¶
- Admin footer i18n: Status bar footer now re-renders on language switch instead of requiring a page refresh
- Docker non-semver build:
make build-docker V=dev-testno longer fails — non-semverVvalues fall back to installing from local wheel instead ofpip install ==<version>
v0.6.5 — 2026-06-02¶
Added¶
- API key label filter — new dropdown on the Request Log tab to filter entries by API key name
- Client IP logging — extracts client IP from
X-Forwarded-For/X-Real-IP/ TCP peer address and displays it in a new "Client IP" column on the Request Log tab - System clock — live-updating clock in the admin header for correlating log timestamps with current time
- Dual-threshold log retention — success and error request log entries are pruned independently; errors get their own cap (
error_max) so rare failures are not evicted by a flood of successful traffic - DB sizing footer — admin panel footer shows on-disk database size, entry counts per class, and retention caps
Fixed¶
- Provider filter — filter now correctly matches entries by provider display name, with three-tier fallback (
target_provider_name→target_provider→ API type for legacy NULL rows) to handle backfill gaps and disabled providers /healthinfo leak — endpoint no longer exposes the full provider and model list to unauthenticated callers; now returns only{"status": "ok"}- i18n completeness — added missing Chinese translations for footer stats, system time label, filter options, and Client IP column header
Changed¶
- Shim directory layout — provider shims now support grouped subdirectories (e.g.
argo/anthropic/,argo/openai_chat/) - Schema migration —
_migrate_add_columns()is now generic, adding any missing nullable columns in a single pass - CI — switched to pre-commit for lint/type checks, pinned ty version
v0.6.4 — 2026-05-20¶
Added¶
- Tinyleaf-style settings popup: Replace the modal-overlay settings dialog with a lightweight centered popup — click outside or press Escape to dismiss, theme and language via
<select>dropdowns with instant apply, About section with version and project links (GitHub, PyPI, Docker Hub, Docs) - Lightweight host IP detection endpoint:
GET /admin/api/diagnostics/host-ipreads/proc/net/routeonly (microsecond-level, no network calls); proxy URL placeholders auto-update with the correct Docker host IP on page load - Admin login persistence: Login state stored in
localStoragewith 30-minute inactivity auto-logout, logout button in header, password manager compatibility (proper<form>,autocompleteattributes) - Inline delete confirmation: Two-step confirm for models, API keys, and request logs replaces native
confirm()dialogs - Test modal improvements: Cancel button with elapsed timer, chart empty state message, Clone button for providers/models
- Mobile responsiveness: Responsive header with wrapping, horizontally scrollable tabs and tables
Fixed¶
- Argo Anthropic response normalization: Detect and convert OpenAI Chat Completions format responses from Argo's
/v1/messagesendpoint to Anthropic Messages format - Argo Anthropic model-aware thinking:
_normalize_thinkingnow distinguishes models requiringadaptive(e.g.claudeopus47) from those needingenabledconversion - Inline confirm i18n and onclick restore: Add missing
confirm.sure/confirm.yestranslation keys; restore originalonclickhandler after confirmation reverts - Reverse proxy caching: Add
Cache-Control: no-cache, no-store, must-revalidateon all admin API responses; switch test polling to POST - Login overlay loop: Prevent login overlay from dismissing password manager autofill popups
- C901 complexity: Extract
_format_connection_errorhelper fromfetch_upstream_models
Security¶
- Admin login rate limiting: 5 failed attempts trigger a 5-minute IP lockout
Changed¶
- Settings UI simplified: Themes reduced to Light/Dark; theme and language selectors moved from header dropdowns into the settings popup
v0.6.3 — 2026-05-17¶
Added¶
- Full
custom_tool_callsupport for OpenAI Responses API: Handle thetype: "custom"tool type end-to-end — request ingestion (coerce to IRtype: "function"with_passthroughfor round-trip), response parsing (custom_tool_callitems with plain-textinput), and streaming (response.custom_tool_call_input.delta/doneevents). Cross-provider degradation synthesizes a single-string-param JSON Schema so custom tools remain usable on Anthropic/Google tool_typefield on IRToolCallStartEvent: Streaming events now carrytool_type("function", "custom", etc.) so converters can emit the correct provider-specific event types- Argo shims with
model_id_fieldandupstream_modelalias: Newargo_openai,argo_anthropic,argo_googleprovider shims that rewrite the model field name for Argo-proxied endpoints. Includes thinking normalization transform forargo_anthropic - Async server-side test tasks: Admin panel test requests now run in background tasks, preventing browser connection pool exhaustion on slow models
- Admin login rate limiting: Brute-force protection on the admin login endpoint
Fixed¶
- Stored XSS in admin UI: Escape single quotes in the
esc()helper to prevent injection via provider/model names custom_tool_callstreaming type loss in gateway:OpenAIResponsesStreamContext.from_base()now copies_tool_call_types, fixing custom tools falling back tofunction_callevent types during IR→provider streaming- Admin UI regressions: Fix infinite recursion in fetch models checkbox handler, allow API key editing regardless of
credential_visiblesetting, remove prefix real-time preview input lag, fix fetch models prefix losing selections, abort test requests on modal close - Reasoning test
max_tokenstoo small: Enforcebudget_tokens >= 1024for reasoning capability tests - httpclient AsyncClient serialization lock: Update vendored httpclient to v0.4.1, use per-task AsyncClient for test self-calls to avoid deadlock
- ty type-check errors: Resolve compatibility issues with ty 0.0.32+
Changed¶
- Admin routes split into subpackage: Refactored monolithic
routes.pyintoroutes/with dedicated modules for auth, config, keys, observability, and testing - CI switched to pre-commit: Linting now uses
pre-commit run --all-files(ruff + ty); complexipy suspended pending upstream fix
v0.6.2 — 2026-05-15¶
Added¶
- Admin password protection:
server.admin_passwordin config enables a login overlay for the admin panel, using HMAC-based session tokens - Credential visibility control:
server.credential_visible: falsehides API key viewing/copying across the admin UI - Provider cascade delete: Deleting a provider now shows affected models and cascade-deletes them
Fixed¶
- Base URL overwrite: Switching provider type no longer overwrites user-entered base URLs
- Request log collapse: Expanded error detail rows persist across auto-refresh
Changed¶
- Zero-dependency on Python ≥3.11: Replaced PyYAML with vendored zerodep yaml module
v0.6.1 — 2026-05-15¶
Added¶
/v1/embeddingspassthrough endpoint: Proxy embedding requests directly to upstream providers without IR conversion — the OpenAI embeddings format is universal across compatible providers. Includes metrics and request log instrumentation/v1/modelsenriched response: Model listing now includesapi_standard(e.g."openai_chat","anthropic") and per-modelcapabilitiesfields- "Fetch from Provider" in admin panel: Query upstream
/v1/models(or equivalent) endpoint from the Models tab, browse available models with checkboxes, and bulk-add with optional prefix. Already-existing models shown as disabled - Model management enhancements: Provider filter dropdown and model name search in the Models tab
- Embedding capability and test type:
embeddingcapability in the model editor (mutually exclusive withvision/tools). Embedding models get a single Test button that POSTs to/v1/embeddingsand displays dimension count - Reasoning capability and test type:
reasoningcapability with dedicated test that sendsreasoning_effort: "low". Mutually exclusive withembedding - Admin panel tab persistence: Active tab stored in
localStorage, survives page refresh
Fixed¶
- Missing event loop in SOCKS5 proxy tests: Use
asyncio.new_event_loop()as fallback when prior tests have closed the default event loop - Type assertion for httpclient response in fetch_upstream_models: Resolve ty type-check error for
AsyncClient.get()return type
v0.6.0 — 2026-05-15¶
Added¶
- Provider shim layer with declarative YAML directory: Shims are now defined as
provider.yaml+ optionaltransforms.pyfiles undershims/providers/<name>/, automatically discovered and registered at import time - Transform mechanism for provider-specific field adaptation: Three composable primitives —
strip_fields(),rename_field(),set_defaults()— handle field-level differences between a provider's API dialect and its base standard - 7 new built-in provider shims: xAI (Grok), Qwen (DashScope), Moonshot (Kimi), MiniMax, Zhipu (GLM), OpenRouter, Volcengine — each with provider-specific transforms where needed
- Gateway proxy applies shim transforms: The gateway request/response pipeline now applies
to_transformson outbound requests andfrom_transformson inbound responses and stream chunks - Provider logos in admin panel: Provider shims can declare a
logoURL (SVG), displayed in the admin panel provider cards - SOCKS5 proxy support restored: Updated vendored
httpclientfrom zerodep v0.3.1 to v0.4.0, which includes full SOCKS5 proxy support (RFC 1928/1929, with username/password authentication). Both--proxy socks5://...CLI flag and"proxy": "socks5://..."config entries now work for all upstream requests
Changed¶
- Shim system refactored to declarative YAML: Replaced programmatic
builtins.pywith a directory-based system (shims/providers/*/provider.yaml+transforms.py). Adding a new provider now requires only YAML + optional Python, no changes to core code - Vendored
validateupdated to zerodep v0.5.0: AddsFieldValidatorandmodel_validatorfor field-level transform+validate pipelines
Removed¶
ModelShimclass removed: Model-level metadata removed in favor of simpler provider-only shims. TheProviderShimdataclass no longer has amodelsfield
Refactored¶
- Zero-dependency gateway (#178): Replaced Starlette + uvicorn + httpx with vendored zerodep
httpserverandhttpclientmodules. The[gateway]extra now has zero external runtime dependencies
Fixed¶
- Deep-merge properties in schema flattening (#161): Fix
$ref/$defsresolution to deep-merge properties and strip orphanedrequiredentries - Unconditional usage fallback and StreamContext merge (#176): Guard against missing usage data and ensure StreamContext state is properly merged
Known Issues¶
- Google tool schema
requiredvalidation (#161): Some Anthropic tool schemas haverequiredentries referencing properties not defined in the schema, causing Google API to reject withINVALID_ARGUMENT
v0.5.3 — 2026-04-25¶
Added¶
- OpenAI Chat converter: thinking config support (#170): The OpenAI Chat converter now handles
reasoning_configin IR requests, mapping to OpenAI'sreasoning_effortparameter. Enables thinking/extended thinking configuration when routing through the Chat Completions API - OpenAI Chat converter:
reasoning_contentfield handling: Non-streaming and streaming responses from reasoning models (e.g., o1, o3) now correctly extract thereasoning_contentfield and convert it to IRReasoningPart, preserving chain-of-thought content during cross-provider conversion - Upstream error body in admin request log: When an upstream provider returns an error, the response body is now included in the admin request log entry, making it easier to diagnose upstream failures without checking server logs
- Copy entry buttons for providers and models in admin page: Provider and model entries in the admin panel now have copy/duplicate buttons for quickly creating new entries based on existing configurations
Fixed¶
FilePartexcluded fromUserContentPart(#160, #162):UserContentPartunion type did not includeFilePart, causingvalidate_ir_request()to reject any user message containing file content (e.g., PDF attachments sent by Claude Code as Anthropicdocumentblocks). The bidirectional conversion logic was already implemented for Anthropic (document), Google (inlineData), and OpenAI Responses (input_file) — only the type definition was missinggoogle_genai/content_ops.pyunconditionalhttpximport (#163): Replacedhttpxwithurllib.requestin the Google GenAI content converter for image URL downloads.httpxwas only declared as a[gateway]optional dependency but was imported unconditionally, causingModuleNotFoundErrorwhen installed without[gateway]extra- Emoji icons replaced with SVG in API key management: API key action buttons in the admin panel used emoji characters that rendered inconsistently across platforms. Replaced with inline SVG icons and added a key visibility toggle button
- API key column layout shift: Fixed CSS layout issue where the API key column width changed when toggling key visibility, causing adjacent buttons to shift position
- Wheel path glob collision with extras brackets: Quoted the wheel file path in CI install commands to prevent shell glob expansion when the filename contains
[extras]bracket syntax
Refactored¶
- SQLite persistence backend: Replaced the JSONL-based request log and JSON-based metrics persistence with a unified SQLite backend. Provides better write durability, atomic operations, and eliminates log rotation complexity. Vendored
persistdictfrom zerodep (v0.4.1) as the key-value storage layer
CI/Build¶
- Install smoke tests: Added CI smoke tests that verify
pip installsucceeds for bothllm-rosetta(core) andllm-rosetta[gateway]variants, catching missing or circular dependencies early
v0.5.2 — 2026-04-19¶
Fixed¶
- Streaming round-trip event inflation (#157): Fixed multiple scenarios where
Provider A → IR → Provider Bstreaming conversion produced more output events than input events:- OpenAI Chat, Anthropic, and Google GenAI converters emitted redundant
content_block_endevents when no content block was open, inflating the output stream - Google GenAI compound chunks (text + finish in the same SSE frame) triggered duplicate text and finish events. Deferred text/finish payloads via
StreamContext.pending_text/pending_finishso they merge into a single event - Tool call events generated spurious
content_block_start/content_block_endwrappers in non-Anthropic targets. Suppressed via_startedlifecycle guard
- OpenAI Chat, Anthropic, and Google GenAI converters emitted redundant
Refactored¶
- Unified
stream_response_to_providerdispatch (#157): Extracted identical dispatch logic (10-entry_TO_P_DISPATCHtable + dispatch skeleton) from all 4 provider converters intoBaseConverter. Each converter now only implements a provider-specific_post_process_to_providerhook (OpenAI Chat injects envelope fields; OpenAI Responses injectssequence_number). Net reduction: ~27 lines StreamContextbuffer convenience methods: Addedbuffer_usage()/pop_pending_usage()/buffer_finish()/pop_pending_finish()to replace manual set-and-clear patterns across all converters
Changed¶
- Pinned dev tooling versions:
ty>=0.0.31andruff>=0.15.0now declared inpyproject.tomldev dependencies. CI no longer installs them separately — uses versions frompip install -e ".[all]" - Converter tests added to CI:
tests/converters/(1086+ tests) now runs in GitHub Actions alongsidetests/test_types/ - Roundtrip inflation regression test: New pytest-parametrized test suite (
tests/converters/test_roundtrip_inflation.py, 15 cases) verifieslen(output_events) <= len(input_events)for all 4 providers across text, reasoning, tool call, and compound scenarios
v0.5.1 — 2026-04-15¶
Added¶
tool_opsconvenience API (#148): New top-levelllm_rosetta.tool_opsmodule for standalone tool definition conversion without instantiating full converter pipelines. Providesto_provider()/from_provider()unified dispatch and per-provider shortcuts (to_openai_chat(),to_anthropic(), etc.). All imports are lazy- Multi-key API management: Admin panel now supports multiple API keys per gateway with per-key labels, create/reveal/delete operations, and usage tracking in request logs
- Gateway API key authentication: Configurable API key (
server.api_key) protects AI request endpoints (/v1/*). Supports format-native credential extraction — OpenAIAuthorization: Bearer, Anthropicx-api-key, Googlex-goog-api-key/?key=query param. When no key is configured, all requests pass through (backward compatible) - Provider enable/disable: Each provider now supports an
enabledfield (defaulttrue). Disabled providers and their models are silently excluded from routing - Docker support: Official
Dockerfile,docker-compose.yml, and Makefile targets (build-docker,push-docker,run-docker) for containerized deployment. Alpine-based image with non-root user, config volume mount, and PUID/PGID support - Admin panel enhancements:
- Provider toggle switches (enable/disable without deleting)
- Model search and column sorting
- Provider rename with automatic model reference updates
- Network diagnostics button (connectivity check + proxy test)
- Model testing with collapsible raw request/response details and image preview for vision tests
- Embedded test image (base64 data URI) to avoid external network downloads
reasoning_effort: 'low'for reasoning model tests to limit token budget
Changed¶
- Admin panel authentication removed from gateway: Admin panel endpoints (
/admin/*) no longer require the gateway API key. Admin access control is delegated to the reverse proxy (e.g. Caddy, Nginx). The gateway API key now only authenticates AI request endpoints (/v1/*) - C901 cyclomatic complexity enforced at threshold 15: Progressive reduction from 25 → 20 → 15 across all converters and gateway modules. Extracted cross-provider consistency helpers (
_build_ir_usage,_build_provider_usage,_convert_tools_from_p,_apply_tool_config) with identical names across all 4 converters BaseConverterabstract methods: Four new abstract methods formalize the cross-provider helper pattern. Preserve-mode hooks documented as convention for providers supporting lossless round-trip- Vendored
validate.pyupdated to zerodep v0.4.2: Internal refactor of monolithic_validate()into focused helpers; no functional changes
Fixed¶
- User-Agent header for image URL downloads: Google GenAI content converter now sends
User-Agent: llm-rosetta/1.0 (image fetch)when downloading image URLs for inline base64 conversion, preventing 403 Forbidden from servers like Wikimedia - Image URL download with proxy support: Image downloads in the Google GenAI converter now respect
HTTPS_PROXY/HTTP_PROXYenvironment variables - Empty content fallback for reasoning models: Admin panel test results now correctly handle
content: ""(from reasoning models where allmax_tokensare consumed by reasoning tokens) instead of showing raw JSON - Config file not found error: Gateway now shows a friendly error message when the config file doesn't exist, instead of a Python traceback
- ty type checker compatibility: Added
ty: ignoreannotations for TypedDict vsdict[str, Any]mismatches andFinishReasonLiteral type narrowing - Google converter crash when thinking consumes all tokens (#152): Gemini 2.5 Pro with small
max_tokenscould have all tokens consumed by thinking, producing a response with no content parts. The converter now falls back to an empty assistant message instead of failing IR validation
v0.5.0 — 2026-04-12¶
Added¶
- Gateway Admin Panel: Built-in web admin panel at
/admin/for managing gateway configuration, monitoring traffic, and inspecting request logs without editing config files or restarting the server- Configuration tab: Visual management of providers (add, edit, rename, delete) and model routing with capabilities (text/vision/tools)
- Dashboard tab: Real-time metrics with summary cards (total requests, error rate, active streams, uptime), rolling 60-second throughput and latency charts, per-provider breakdown
- Request Log tab: Filterable request log with model, provider, and status filters, paginated view with color-coded status codes
- 8 themes: Light, Indigo Dark, Dracula, Nord, Solarized, Osaka Jade, One Dark, Rosé Pine — persisted in localStorage
- i18n: English and Chinese language support with localStorage persistence
- File-based persistence: Metrics counters (JSON) and request log (JSONL) are automatically saved to disk alongside the config file. Data survives server restarts. Log rotation with gzip compression (2 MB limit, 3 backups)
- Provider rename: Renaming a provider automatically updates all model routing references
- API key security: Masked keys on provider cards, reveal-on-demand with visibility toggle and copy button in edit modal. Masked values are never written back to config
Changed¶
- Provider names decoupled from API standard types: Provider names are now user-defined strings (e.g.
"my-openai","OpenRouter_anthropic") instead of being constrained to the 4 standard type identifiers. A separatetypefield specifies the API standard (openai_chat,openai_responses,anthropic,google) - Extracted
write_config()toconfig.pyfor shared use by CLI and admin panel
v0.4.2 — 2026-04-11¶
Changed¶
ReasoningConfig.enabledreplaced withmodefield: The booleanenabledfield has been replaced bymode: Literal["auto", "enabled", "disabled"]. This aligns the IR more closely with provider semantics (Anthropic's three-waythinking.type, OpenAI Responses'reasoning.type). Omittingmoderetains the previous "provider default" behavior. Theeffortfield now lives directly inReasoningConfigrather than being nested
Fixed¶
- Responses API
developerrole mapping: The OpenAI Responses API usesrole: "developer"(equivalent to Chat's"system"). Previously this role was passed through to IR unchanged, causing validation failures. Now correctly mapped to IR"system"during Provider→IR conversion - Google GenAI
additionalPropertiesrejection: Google's function_declarations API rejects theadditionalPropertiesJSON Schema keyword. Addedextra_strip_keysparameter tosanitize_schema()so providers can strip provider-specific unsupported keywords. Google tool_ops now stripsadditionalPropertiesrecursively from nested schemas - Google GenAI
prompt_tokens_detailsformat mismatch: Google returns modality token details aslist[ModalityTokenCount](e.g.[{"modality": "TEXT", "token_count": 42}]) but IR expectsdict[str, int](e.g.{"text_tokens": 42}). Added bidirectional conversion helpers_modality_list_to_dict()and_dict_to_modality_list(). Handles both SDK (token_count) and REST API (tokenCount) field names - Cross-format tool call ID prefix mapping: The Responses API enforces
fc_prefix on tool call IDs, but Chat usescall_and Anthropic usestoolu_. Added automatic prefix mapping during Responses conversion to prevent validation failures in cross-format scenarios - Adaptive thinking fallback: When converting IR reasoning config to Anthropic format,
mode: "enabled"withoutbudget_tokensnow correctly falls back to{"type": "adaptive"}with a warning, instead of producing an invalid{"type": "enabled"}without the requiredbudget_tokens
v0.4.1 — 2026-04-10¶
Added¶
force_conversionparameter forconvert(): Newforce_conversion: bool = Falsekeyword-only parameter. WhenTrue, the full source→IR→target pipeline runs even when source and target providers match, ensuring parameter normalization (e.g.max_tokens→max_completion_tokensfor OpenAI Chat). DefaultFalsepreserves existing passthrough behavior
Fixed¶
- Vendored
validate.pyupdated from zerodep v0.4.1: Applied pyupgrade fixes —Callableimported fromcollections.abcinstead oftyping(UP035),@functools.cachereplaces@functools.lru_cache(maxsize=None)(UP033) - Removed unused
sysimport in benchmark script - Applied
ruff formatto benchmark scripts
Changed¶
- Removed incorrect "Related Projects" section from README — LLM-Rosetta is an independent project, not part of the ToolRegistry ecosystem
v0.4.0 — 2026-04-09¶
Added¶
- Metadata preservation for lossless A→IR→A round-trip (#60, PR #119): New
MetadataMode("strip"/"preserve") option inConversionContextthat captures provider-specific fields duringfrom_providerand re-injects them duringto_provider, enabling lossless round-trip conversion. Helper methods onConversionContext:store_request_echo(),store_response_extras(),store_output_items_meta(),get_echo_fields(),get_output_items_meta(). Per-provider coverage:- OpenAI Responses: captures/restores 28+ echo fields (temperature, tools, reasoning, truncation, etc.), per-output-item metadata (id, status, annotations, logprobs),
RESPONSES_REQUIRED_DEFAULTSdict for spec-required fields with sensible defaults,sequence_numberon all SSE events - Anthropic: preserves
stop_sequence,container, citations, and OpenRouter extension usage fields - OpenAI Chat: now re-emits
refusalandannotationsfields inresponse_to_provider(previously dropped) - Google GenAI: preserves
promptTokensDetailsandcachedContentTokenCountin usage metadata - Gateway: automatically enables preserve mode for both streaming and non-streaming paths; bridges metadata between
from_ctxandto_ctxduring streaming
- OpenAI Responses: captures/restores 28+ echo fields (temperature, tools, reasoning, truncation, etc.), per-output-item metadata (id, status, annotations, logprobs),
Fixed¶
- Open Responses spec compliance for streaming and non-streaming: Added required fields to all SSE events (
item_id,logprobs,annotations,status,sequence_number,output_index,content_index), usage detail breakdowns (output_tokens_details,input_tokens_details), message item IDs and status for non-streaming output items,function_callstatus field in tool_ops,service_tierdefault to"default"(string, not null per spec),completed_atin required defaults,created_atfallback to current time when not provided, normalized echoed tools withstrict: null, and metadata bridging fromfrom_ctxtoto_ctxin gateway streaming. All 6 Open Responses compliance tests now pass (schema + semantic)
v0.3.1 — 2026-04-07¶
Fixed¶
service_tier: Noneandsystem_fingerprint: Nonecausing validation errors (PR #118): OpenAI upstream returns these fields asnull, but the existence check (if "key" in dict) passed and assignedNoneto IR'sNotRequired[str]field. Changed to value-not-None check in both OpenAI Chat and OpenAI Responses converters. Discovered via Oaklight/argo-proxy#99- Base
StreamContextmissing provider-specific attributes in Responses streaming (PR #118): When a gateway passes a baseStreamContexttoOpenAIResponsesConverter.stream_response_to_provider(), the method accessesaccumulated_text,output_item_emitted, etc. that only exist onOpenAIResponsesStreamContext. Added auto-upgrade viafrom_base()classmethod with metadata caching to preserve state across calls
v0.3.0 — 2026-04-07¶
Added¶
- Multimodal tool result support across all 4 converters (#92, PR #109): Tools can now return multimodal content (text + images + files) as
ToolResultPart.result. Three providers (Anthropic, OpenAI Responses, Google GenAI) support this natively; content blocks are converted through each provider'scontent_opslayer. See provider support matrix below - Lossless multimodal tool result roundtrip for OpenAI Chat (#92, PR #108): OpenAI Chat Completions only accepts
content: stringfor tool messages. Implements a dual encoding strategy — tool message keepsjson.dumps(result)as data fallback, plus a synthetic user message carries visual content (image_urlparts) wrapped in<tool-content call-id="...">XML tags. Unpacking recovers multimodal structure from the synthetic message (preferred) or falls back to JSON parsing if the synthetic message was trimmed by agent frameworks extract_all_text()helper function (PR #109): Extracts text from bothTextPartandReasoningPartcontent — useful for thinking models (e.g. gemini-2.5-flash) that may place answers in reasoning parts rather than text partsgenerate_chartexample tool (PR #109): New multimodal tool inexamples/tools.pyreturning[TextPart, ImagePart]with inline base64 PNG, plusmultimodal_tools_speccombining all 3 example tools- Multimodal integration tests across all 4 provider SDKs (PR #109): Two new test scenarios per provider — (A) tool returning multimodal content (text + image), (B) image input combined with tool calls. All 30 tests pass against official APIs: OpenAI Chat 9/9, OpenAI Responses 6/6, Anthropic 8/8, Google GenAI 7/7
- Runtime IR validation via vendored zero-dependency validator (#91):
validate_ir_request(),validate_ir_response(), andvalidate_ir_messages()utilities validate IR structures against their TypedDict definitions at runtime. All 4 converters now validate output inrequest_from_provider()andresponse_from_provider(). Replaces manualBaseMessageOps.validate_messages. Includes Python <3.11 compatibility fortyping_extensions.TypedDict - Constants validation tests: 39 new tests across 4
test_constants.pyfiles verifying that all reason mapping values are valid IR finish reasons, mapping coverage is complete, event type constants are well-formed, and ID generation produces correct formats - Finish reason mapping test coverage: 38 tests validating reason mapping correctness as a safety net for the constants refactoring
ConversionContextbase class for conversion pipelines (#106, PR #111): NewConversionContextdataclass withwarnings: list[str],options: dict[str, Any], andmetadata: dict[str, Any]— a structured context container for non-streaming conversions. NewBaseConverter.create_conversion_context(**options)factory method mirrors the existingcreate_stream_context(). All 6 non-streamingBaseConvertermethods now accept an optionalcontext: ConversionContextkeyword parameter; converter implementations sync warnings tocontext.warnings. Gateway proxy creates a shared context per request and passes it through the full source→IR→target→response pipeline
Fixed¶
- Contextual error messages for tool conversion failures (#85, PR #110): When
p_tool_definition_to_ir()fails on a malformed or unsupported tool definition, theValueErrornow includestype=andname=context so users can identify which tool caused the issue. Applied to all 4 converters (OpenAI Chat, OpenAI Responses, Anthropic, Google GenAI) with unit tests - OpenAI Responses
tool_choiceformat (PR #109): Was using Chat Completions format ({"type": "function", "function": {"name": "..."}}); now uses Responses format ({"type": "function", "name": "..."}) - OpenAI Responses tool call ID round-trip (PR #109): Responses API uses
fc_prefix IDs while IR usescall_prefix. The Responsesidis now preserved inprovider_metadataseparately fromcall_id, enabling lossless round-trip conversion - OpenAI Responses reasoning item round-trip (PR #109): Reasoning models (e.g. gpt-5-nano) emit reasoning items with
id(rs_ prefix), structuredsummaryarrays, andencrypted_content. These are now preserved throughprovider_metadatafor lossless round-trip — fixes 400 errors when reasoning items were sent back without their originalid - IR validation accepts
Nonefor optional response fields (PR #109):logprobsandsystem_fingerprintinIRResponsenow acceptNonevalues (previously only accepted missing keys) - OpenAI Responses
content_filterfinish reason mapped to wrong status (#90):content_filterwas incorrectly mapped to"completed"status inresponse_to_providerandstream_response_to_provider. Now correctly maps to"incomplete"status withincomplete_details.reason = "content_filter" - Anthropic streaming missing
refusalreason mapping: The streamingreason_mapwas missing therefusalentry present in the non-streaming path, causing Anthropic refusal stop reasons to be silently dropped during streaming. Fixed as a side effect of the constants extraction (#64) — both paths now share the sameANTHROPIC_REASON_FROM_PROVIDERdict
Changed¶
ReasoningConfig.effortexpanded to 5-level enum (#100): Effort levels now include"minimal","low","medium","high","max". Provider-specific mappings: Anthropic maps tothinking.type="adaptive"withthinking.effort; OpenAI Chat/Responses clamp"minimal"→"low"and"max"→"high"(with warnings); Google GenAI maps tothinking_config.thinking_levelReasoningConfig.typereplaced withReasoningConfig.enabled(#70): Thetype: Literal["enabled", "disabled"]field is replaced withenabled: boolto avoid shadowing the Python built-intypeand provide a more natural API- Merged duplicate IR concepts (#69): Removed
candidate_countfromGenerationConfig— useninstead (Google GenAI converter mapsn↔candidate_countinternally). Unifiedsystem_instructiontype fromstr | list[dict]tostr - Normalized
ImagePart,FilePart,AudioPartto canonical forms (#68): Each part now has exactly two canonical forms — URL reference + structured inline data (e.g.image_data) — plus a unifiedprovider_ref: dict[str, Any]for provider-specific references. Removed redundant top-leveldata/media_typefields and replacedfile_id/audio_idwithprovider_ref - IR type fields changed from
Iterabletolist; function parameters toSequence(#67): TypedDict fields now uselistfor indexable, serialization-friendly semantics; function parameters useSequence(covariant, read-only). Also fixes a latent generator-consumption bug instrip_orphaned_tool_config StreamContextnow inherits fromConversionContext(#106, PR #111):StreamContextis a subclass ofConversionContext(IS-A relationship), unifying the context model for streaming and non-streaming paths. File renamed:base/stream_context.py→base/context.pyStreamContextconverted to dataclass with provider subclass (#65):StreamContextis now a@dataclasswith typed fields (eliminates defensivegetattr/hasattrpatterns). OpenAI Responses-specific state extracted intoOpenAIResponsesStreamContextsubclass. NewBaseConverter.create_stream_context()factory method
Refactored¶
- Warnings single-source convergence (#113, PR #115): All 4 converter
request_to_providermethods now useConversionContextas the single accumulation point for warnings. Eliminates the dual-write pattern where warnings were written to both a local list andcontext.warnings. The returned warnings list IS the same object ascontext.warnings— no duplication possible ProviderMetadataStorereplaces global metadata cache (#112, PR #117): The module-level_provider_metadata_cachedict inproxy.pyis replaced withProviderMetadataStore— a class with TTL-based expiration (30 min), max-size eviction (10k entries), and explicit lifecycle management. The store is created per-app increate_app()and passed viaapp.state, eliminating implicit global mutation.close_clients()renamed toclose_resources()to also clear the store on shutdown- Shrink public API export surface (#114, PR #116): Reduced
__all__exports across converter packages to only the primary converter class, removing internal implementation details (*MessageOps,*ContentOps,*ConfigOps,*ToolOps,*Constants) from the public API. Internal modules remain importable for advanced use but are no longer promoted as public surface - Extracted stream event handlers from monolithic methods (#63): Replaced 8 monolithic
if/elifstream methods (~1,781 lines) across all 4 converters with individual handler methods dispatched via class-level handler tables. Public API unchanged - Extracted shared utility functions in OpenAI Responses converter (#66):
resolve_call_id()andbuild_message_preamble_events()extracted fromconverter.pyintoutils.pywith dedicated unit tests - Extracted per-provider constants for reason mappings and magic values (#64): Inline reason mapping dicts, SSE event type string literals, status-to-reason conditional logic, and ID generation patterns across all 4 converters are now centralized in per-provider
_constants.pymodules. IncludesAnthropicEventTypeandResponsesEventTypeclasses,REASON_FROM_PROVIDER/REASON_TO_PROVIDERdicts, andgenerate_tool_call_id()/generate_message_id()helpers
v0.2.6 — 2026-03-29¶
Fixed¶
- Chat Completions tool message ordering after Responses API conversion (@caidao22): Codex CLI interleaves
function_call_outputwith other items (e.g. user warnings) in Responses API format — valid there since items match bycall_id. But after IR → Chat Completions conversion, the interleaved messages break the OpenAI Chat API constraint thatrole: "tool"messages must immediately follow theirassistanttool_calls, causing upstream 400 errors. Added_reorder_tool_messages()post-processing inOpenAIChatMessageOps.ir_messages_to_p()that groups tool responses back to their corresponding assistant messages - Orphaned
tool_choice/tool_configstripped when no tools defined (@caidao22): Codex context compaction can drop all tool definitions while keepingtool_choice(e.g."auto"), causing upstream APIs to reject with "tool_choice is set but no tools are provided". Addedstrip_orphaned_tool_config()in all four converters — part of the same Codex compaction fix family asfix_orphaned_tool_calls_ir(orphaned tool_call/result pairing) and_reorder_tool_messages(tool message ordering). Also extendedfix_orphaned_tool_calls_irto Google GenAI converter for completeness (#87) - Stream event ordering:
UsageEventis now emitted beforeFinishEventin all four provider converters (OpenAI Chat, OpenAI Responses, Anthropic, Google GenAI). PreviouslyFinishEventwas processed first, causingresponse.completedto carryoutput_tokens=0— downstream consumers (e.g. Codex token tracking) saw stale usage data. For cross-chunk scenarios (OpenAI Chat sendsfinish_reasonandusagein separate chunks),FinishEventnow defersresponse.completedtoStreamEndEventwhich merges any pending usage - Parallel tool calls merged into one in Anthropic/Google → Chat streaming: Anthropic and Google GenAI
stream_response_from_provideremittedToolCallStartEventandToolCallDeltaEventwithouttool_call_index. When routing to Chat Completions, all parallel tool calls defaulted to index 0, causing the client SDK to merge them into a single call. Anthropic now derivestool_call_indexfromcontext._tool_call_orderposition; Google computes it from registration order in context (#88, #89) - Missing
idfield on Responsesfunction_calloutput: Non-streamingresponse_to_providerwas missing theidfield onfunction_calloutput items. Streaming used a syntheticfc_prefix that could leak into IR viap_tool_call_to_irfallback path. Unified both paths to usecall_iddirectly asid(no prefix) - Responses streaming
item_idand emptytool_call_idresolution (@caidao22): Addeditem_idtracking toStreamContext(tool_call_item_id_map, bidirectional mapping). Responsesstream_response_to_providernow emitsitem.idonoutput_item.addedanditem_id(notcall_id) onfunction_call_arguments.delta/doneevents. Defense-in-depth: resolves emptytool_call_idbytool_call_indexvia context (#86) - Non-function tool names mangled with type prefix (@caidao22): Non-function IR tool definitions (e.g.
type="custom",name="apply_patch") were converted with a type prefix (custom_apply_patch), breaking tool_call matching since the client expects the original name. Both OpenAI Chat and Responses converters now useir_tool["name"]directly (#84)
v0.2.5 — 2026-03-23¶
Fixed¶
- Anthropic
input_schemamissingtypefor parameterless tools: MCP tools with no parameters produceinput_schema: {}, but Anthropic requires"type"to be present. Now defaults to{"type": "object"}when the schema dict lacks atypefield — fixestools.0.custom.input_schema.type: Field requirederrors when routing Google GenAI or OpenAI Responses tool calls to Anthropic upstream - Google GenAI camelCase field handling across the full converter stack: Gemini CLI and the Google REST API use camelCase (
inlineData,fileData,mimeType,fileUri,functionCall,functionResponse,finishReason,usageMetadata,responseMimeType,responseSchema,thinkingConfig,maxOutputTokens,stopSequences, etc.), but the converter only accepted snake_case. All P→IR methods in content_ops, config_ops, tool_ops, message_ops, and converter now accept both conventions; all IR→P methods now output camelCase for REST API compatibility - Image/audio/file data lost during Google→IR conversion:
p_part_to_irchecked forinline_data(snake_case) but Gemini CLI sendsinlineData(camelCase) — binary content was silently dropped with a不支持的Part类型warning. Fixed by normalizing camelCase keys at the dispatch entry point - Cross-format image conversion failure (Google → OpenAI/Anthropic): Google's
p_image_to_irproducesImagePartwith top-leveldata+media_typefields, but OpenAI Chat, Anthropic, and OpenAI Responsesir_image_to_ponly checkedimage_urland nestedimage_data— threwValueError. All three target converters now handle top-level fields as a fallback path (#68) - Google GenAI tool_call_id reconciliation: Google
functionCallhas no ID field, so UUIDs are generated during P→IR. But Gemini CLI assigns its own IDs tofunctionResponse(format:name_timestamp_index), creating a mismatch. New_reconcile_tool_call_idsmethod matches tool results to tool calls by function name, fixing orphaned tool_call errors - tool_call_id exceeds OpenAI 40-character limit: Generated IDs used
call_{name}_{8hex}format — MCP tool names likemcp_toolregistry-hub-server_datetime-nowproduced 54-char IDs. Shortened tocall_{24hex}(fixed 29 chars) - Google→IR role mapping for tool results:
functionResponseparts producedrole: "user"IR messages, sofix_orphaned_tool_calls_ir(which checksrole: "tool") couldn't detect them. Now separatesfunctionResponseintorole: "tool"messages with explicit"tool": "user"in_IR_TO_GOOGLE_ROLE - Mixed content message ordering: When a Google message contains both
functionResponseandinlineData, the content parts were emitted before tool results, breaking OpenAI's requiredassistant(tool_calls) → tool(response)ordering. Tool results now precede content parts in the split - Google built-in tools (googleSearch, codeExecution):
p_tool_definition_to_irnow returnsNonefor tool entries without anamefield; converter skips them instead of producing emptyfunction.nameerrors - Gateway: Starlette
on_shutdowndeprecation: Replaced deprecatedon_shutdownparameter withlifespanasync context manager — fixes compatibility with Starlette 0.38+ which removedon_shutdown/on_startup
Added¶
- StreamContext:
get_tool_call_args()andget_pending_tool_calls()methods for querying accumulated tool call state during streaming
Changed¶
BaseToolOps.p_tool_definition_to_irreturn type: NowToolDefinition | list[ToolDefinition] | Noneto support unconvertible tool entries
Added (Documentation)¶
- Provider & CLI Compatibility Matrix: New guide page documenting real-world issues found during live integration testing with Gemini CLI, Claude Code, and OpenCode through format-converting proxies
v0.2.4 — 2026-03-22¶
Added¶
fix_orphaned_tool_calls()utilities: Public functions inconverters/openai_chat/tool_ops.py,converters/openai_responses/tool_ops.py, andconverters/anthropic/tool_ops.pythat detect mismatched tool calls/results and fix them bidirectionally — injecting synthetic placeholder results for orphaned calls and removing orphaned results without matching calls. OpenAI (Chat & Responses) and Anthropic strictly require this pairing (return 400 otherwise); only Google Gemini is lenient. Automatically applied at the IR level duringrequest_to_provider()for all strict-pairing converters; emitsWARNING-level log when orphaned tool calls or results are detected (#82, #84)
Fixed¶
- Anthropic→IR role normalization for
tool_resultmessages: Anthropic placestool_resultblocks inrole: "user"messages, but IR usesrole: "tool"(like OpenAI). The Anthropic converter now normalizes puretool_resultuser messages torole: "tool", and splits mixedtool_result+ text messages into separaterole: "tool"androle: "user"IR messages. This fixesfix_orphaned_tool_calls_ir()failing to detect answered tool calls in cross-format conversions (e.g. Anthropic → OpenAI Chat) (#84) - OpenAI Responses→IR role normalization for
function_call_outputitems:function_call_outputandmcp_call_outputitems were grouped intorole: "user"IR messages, but IR usesrole: "tool"for tool results. The Responses converter now groups these items intorole: "tool"messages, fixingfix_orphaned_tool_calls_ir()failing to detect answered tool calls when converting Responses → other formats (e.g. Responses → OpenAI Chat) (#84)
Added (Documentation)¶
- Provider Dialect Differences guide: New section in the Converters guide (EN + ZH) documenting tool schema sanitization, orphaned tool call handling, and Google camelCase/snake_case differences
v0.2.3 — 2026-03-22¶
Fixed¶
- Tool schema sanitization applied to all converters:
_sanitize_schema()was previously only called in the OpenAI Chat converter. Google GenAI, OpenAI Responses, and Anthropic converters now also sanitize tool parameter schemas before sending to upstream, preventing rejections from strict endpoints like Vertex AI (#80) - Non-standard
refand$schemakeywords stripped: OpenCode's built-in tools use a barereffield (without$prefix) and$schemaat the top level, both rejected by Vertex AI. Added to the unsupported keywords blocklist (#80) $ref/$defsresolved by inlining: JSON Schema$refreferences are now resolved by inlining the referenced definition from$defs/definitions, and both keys are removed from the output. Supports nested and chained references (#80)- Streaming tool call arguments not accumulated: OpenAI Chat, Anthropic, and Google GenAI converters registered tool calls in
StreamContextbut never calledappend_tool_call_args()to accumulate argument deltas during streaming. This caused tool call arguments to arrive empty at upstream (e.g., MCP tools returning'query' is a required property). Only the OpenAI Responses converter was correct (#81) - OpenAI Chat streaming tool call ID resolution: Delta-only chunks (carrying
indexbut noid) produced an empty-stringtool_call_id. Now resolves the effective ID fromStreamContext._tool_call_orderusing the chunk index (#81)
Changed¶
sanitize_schemaextracted toconverters/base/tools.py: The schema sanitization utility (previously_sanitize_schemaprivate toopenai_chat/tool_ops.py) is now a public shared function inconverters/base/tools.py, exported viaconverters.base. All 4 convertertool_ops.pyfiles import from the shared location instead of cross-importing fromopenai_chat(#66)
v0.2.2 — 2026-03-22¶
Fixed¶
- Missing
content_block_stopin Anthropic SSE output: When converting OpenAI Chat streaming responses to Anthropic SSE format,content_block_stopevents were not emitted beforemessage_delta, causing Claude Code to silently discard response content. The Anthropic converter now emitscontent_block_stopfor any open content block when processing aFinishEvent(#77) - Upstream preflight chunk misinterpreted as stream end: Argo API sends a preflight chunk with
choices: []and emptyid/modelbefore actual content. The OpenAI Chat converter now only treats empty-choices chunks as stream-end after the stream has actually started (context.is_startedguard) (#77)
v0.2.1 — 2026-03-20¶
Added¶
- Gateway request/response body logging: configurable debug logging with colorized output, body sanitization and truncation — enable via config (
"debug": {"verbose": true, "log_bodies": true}), env vars (LLM_ROSETTA_VERBOSE,LLM_ROSETTA_LOG_BODIES), or--verboseCLI flag - Google
output_format="rest"forrequest_to_provider(): passoutput_format="rest"to get a REST API–ready request body withtools/tool_configat top level and generation params wrapped ingenerationConfig— eliminates the need for manual SDK→REST fixups
Changed¶
- Gateway modularization: split
app.py(1057 lines) intoproxy.py(proxy engine, SSE handling, upstream requests),cli.py(CLI entry point, argparse, subcommands), and a slimmedapp.py(route handlers, app factory, ~210 lines) - Moved Google REST body fixup to core:
_fixup_google_body()logic moved fromgateway/proxy.pyintoGoogleGenAIConverter._to_rest_body(), removing duplicated SDK→REST transforms from the gateway and all 6 REST examples
Fixed¶
- OpenAI Responses streaming: added missing
id/object/modelfields toresponse.completed,output_index/content_indexto text delta events, and proper lifecycle events (output_item.added,content_part.added,content_part.done,output_item.done) (#56) - OpenAI Chat streaming:
tool_callsentries now always include the requiredindexfield, defaulting to0when not explicitly provided by the upstream IR event (#57) - OpenAI Chat streaming: usage-only chunk now includes
"choices": []to satisfy clients that validate everychat.completion.chunkmust contain achoicesarray (#55) stream_options(Chat Completions-only field) no longer leaks into OpenAI Responses API requests — the Responses converter'sir_stream_config_to_p()was incorrectly emittingstream_options, causing upstream rejection when Chat-format clients (Kilo, OpenCode) were proxied to the Responses API (#58)- Google GenAI converter now handles tools and tool_config in REST-format requests (top-level fields) in addition to SDK format (
config.tools) — previously only SDK format was recognized, silently stripping tool definitions from gateway-proxied requests (#59) - Google camelCase
functionDeclarationsnot parsed:p_tool_definition_to_ir()now handles bothfunctionDeclarations(camelCase/REST) andfunction_declarations(snake_case/SDK), and extracts all declarations instead of only the first. Also added camelCase support forfunctionCallingConfig/allowedFunctionNamesandtoolConfigin request parsing — fixes Gemini CLI tool calling through the gateway (#61) - Google streaming tool calls split into two chunks:
stream_response_to_provider()now deferstool_call_startand emits the completefunction_call(name + args) in a single chunk ontool_call_delta, matching the Google API's native format (#62)
v0.2.0 — 2026-03-18¶
Added¶
- Standalone API test scripts (
llm_api_simple_tests/): 20 test scripts (5 per provider) using official SDKs directly, covering simple query, multi-round chat, image, function calling, and comprehensive scenarios — added as a git submodule from Oaklight/llm_api_simple_tests - LLM-Rosetta Gateway: REST gateway application for cross-provider HTTP proxying
- CLI entry point (
llm-rosetta-gateway) and package structure for the gateway - Gateway config auto-discovery at
./config.jsonc,~/.config/llm-rosetta-gateway/config.jsonc,~/.llm-rosetta-gateway/config.jsonc --edit/-eflag to open config file in$EDITOR(falls back to nano/vi/vim)--version/-Vflag showing current version- ASCII art startup banner with
--no-bannerto suppress add provider <name>subcommand for adding provider entries to config (with--api-key,--base-urlflags or interactive prompts; known providers auto-fill defaults)add model <name>subcommand for adding model routing entries (with--providerflag or interactive prompt)- Gateway providers module (
providers.py): centralized provider definitions with auth-header builders, URL templates, default base URLs, and API key env-var names - API key rotation: round-robin
KeyRingfor comma-separated API keys per provider - Proxy support: global
server.proxyand per-providerproxyconfig for HTTP/SOCKS proxies; CLI--proxyflag overrides config - Makefile
test-integrationtarget usingproxychains(if available) for integration tests initsubcommand to create a templateconfig.jsoncat the XDG default location (~/.config/llm-rosetta-gateway/)- Model listing endpoints:
GET /v1/models(compatible with both OpenAI and Anthropic SDKs) andGET /v1beta/models(Google GenAI SDK format) — enablesclient.models.list()across all three SDKs (#54)
Changed¶
- Bumped minimum Python to 3.10+; migrated to stdlib
typing(removedtyping_extensions) - Applied
ruffformatter across the entire codebase - Updated Makefile with
lint,test, andbuildtargets - Added
ty(type checker) configuration - Configured
rufflint rules (E,F,UP) inpyproject.toml; ignoreUP007(Union syntax) andE501(line length) - Modernized typing imports across
src/,tests/,examples/, andscripts/— replacedtyping.Dict,List,Tuple,Optional,Typewith stdlib builtins
Fixed¶
- Streaming crash with Anthropic provider when usage tokens are
null—TypeError: NoneType + intin all converters (replaced.get("*_tokens", 0)with.get("*_tokens") or 0) - Gateway provider
base_urlvalidation — fail early with clear error on config typos likehttps:example.com(missing//) - Added
socksioto gateway dependencies for SOCKS proxy support (httpx[socks]) - Added missing
__init__.pyfortypespackage - Updated
git cloneURL fromllm-rosettatollm-rosettain documentation - Resolved all
tytype checker diagnostics insrc/(31 → 0):- Fixed
is_part_type()TypeGuard narrowing — replaced with specific type guard functions (is_text_part, etc.) - Added missing TypedDict fields:
provider_metadataonTextPart/ReasoningPart,file_idonImagePart/FilePart - Fixed
IRRequest.messagestype fromRequired[Message]toRequired[Iterable[Message]] - Used
cast()to bridgedict[str, Any]intermediates to TypedDict return types - Fixed dict literal type inference conflicts in converter response builders
- Fixed
- Resolved all
tytype checker diagnostics intests/(1506 → 0):- Added
cast()wrappers on dict literals passed to functions expecting TypedDict parameters (GenerationConfig,IRRequest,IRResponse,ToolDefinition,ToolChoice, etc.) - Narrowed
Message | ExtensionItemunion results withcast(list[Any], ...)orcast(Message, ...) - Converted
Iterablecontent fields tolistfor subscript andlen()access - Added
assert ... is not Noneguards before subscripting optional return types - Fixed
FinishReasonfrom bare string to TypedDict form{"reason": "stop"} - Fixed
IRResponse.objectliteral from"chat.completion"to"response"
- Added
- Resolved all
rufflint violations insrc/andtests/(UP035 deprecated imports, F401 unused imports) - Google
thought_signaturepreservation through gateway round-trips — newer Google models requirethoughtSignatureechoed back in function call parts; the gateway now cachesprovider_metadata(includingthought_signature) keyed bytool_call_idand re-injects it on subsequent requests for both streaming and non-streaming modes (#51) - OpenAI Responses converter now handles all 3
inputformats: bare string ("input": "hello"), shorthand list ([{"role": "user", "content": "hi"}]), and structured list — previously only the structured format was supported, causing the OpenAI Python SDK's shorthand items to be silently dropped and producing empty IR messages when cross-converting to Anthropic or Google providers
2026-03-15 — Rebrand to LLM-Rosetta¶
Changed¶
- Project renamed from LLM-Rosetta to LLM-Rosetta across all code, docs, and configuration
- Package renamed from
llm-rosettatollm_rosetta;pyproject.tomlupdated accordingly - Documentation fully rewritten with Zensical for both English (
docs_en) and Chinese (docs_zh) - README (EN/ZH) updated with new branding, badges, and
pyproject.tomlmetadata
2026-03-06 — Streaming & StreamContext¶
Added¶
StreamContextfor stateful stream chunk processing across all 4 providersstream_response_from_provider()andstream_response_to_provider()methods on all convertersaccumulate_stream_to_assistant_message()helper function- Stream abstract methods (
stream_response_to_provider,stream_response_from_provider) added toBaseConverter - 4 new IR stream event types:
StreamStart,StreamEnd,ContentBlockStart,ContentBlockEnd ReasoningDeltaEventandtool_call_indexfield on IR stream types- Cross-provider streaming examples for all provider pairs (SDK and REST variants)
- Local file cache and retry logic for image downloads in examples
Changed¶
- Stream method signatures updated with optional
contextparameter - Deprecated
from_providermethods removed;auto_detectupdated to new API - Obsolete single-provider example scripts removed (replaced by cross-provider examples)
_normalize()extracted toBaseConverteras a shared utility
Fixed¶
- camelCase fallback for Google GenAI REST stream/response fields
- Anthropic stream converter:
thinking_delta,signature_delta,tool_call_idhandling - OpenAI Chat stream converter:
reasoning_content, empty string,tool_call_indexhandling - Missing
__init__.pyfor test package discovery from_providercalls ingoogle_genai_rest_e2eintegration test
2026-02-14 — Cross-Provider Examples & Stream Converters¶
Added¶
- Stream converters for all 4 providers: OpenAI Chat, Anthropic, Google GenAI, OpenAI Responses
- Stream converter unit tests for all providers
- 6 cross-provider conversation examples (SDK-based): OpenAI Chat ↔ Anthropic, OpenAI Chat ↔ Google GenAI, OpenAI Chat ↔ OpenAI Responses, Anthropic ↔ Google GenAI, Anthropic ↔ OpenAI Responses, Google GenAI ↔ OpenAI Responses
- Common resources module for cross-provider conversation examples
- Image URL to inline base64 conversion helpers for Google GenAI compatibility
- OpenAI Responses E2E integration tests (REST + SDK)
- Unit tests for OpenAI Responses Ops classes and converter
- Examples README in English and Chinese
Changed¶
- OpenAI Responses converter restructured to Bottom-Up Ops Pattern
- Post-refactor cleanup: removed deprecated utils and empty directories
Fixed¶
- Image URLs converted to inline base64 for Google GenAI provider compatibility
2026-02-13 — Bottom-Up Ops Architecture¶
Added¶
- Google GenAI converter rebuilt with Bottom-Up Ops Pattern
- TypedDict replicas of OpenAI Responses API types
- TypedDict replicas of Google GenAI SDK types
- Google GenAI REST and SDK E2E integration tests
- Unit tests for
google_genaiconverter Ops classes - Anthropic SDK and REST E2E integration tests
- OpenAI Chat E2E tests split into SDK and REST versions
- GitHub Actions CI/CD workflows and Dependabot configuration
Changed¶
- Anthropic converter redesigned with bottom-up Ops architecture
- Imports updated to use new
google_genaiconverter module - Old
google/converter and legacy tests removed
2026-02-12 — Converter Redesign¶
Added¶
- TypedDict replicas of Anthropic SDK types
- TypedDict replicas of OpenAI Chat types with backward compatibility and tests
- Legacy body converter design preserved as historical reference
Changed¶
- OpenAI Chat converter redesigned with bottom-up Ops architecture
- Ruff lint errors fixed across entire codebase
2026-01-06 — Layered Architecture & Documentation¶
Added¶
- English and Chinese documentation structures initialized (
docs_en,docs_zh) - Comprehensive error handling documentation
- OpenAI Chat Converter integration tests
- Comprehensive mock implementations for
BaseConvertertest class - File handling functionality in base converter
- Provider-to-IR mapping documentation
Changed¶
- Converter base refined with layered abstract template
- All 4 converters restructured with layered architecture (Anthropic, OpenAI Chat, OpenAI Responses, Google GenAI)
- Type annotations updated for IR content/part conversion methods
- IR type system reorganized and enhanced
- English translations added to code comments and docstrings
Fixed¶
- Reasoning content field assertion corrected
- File content handling in OpenAI Chat Completions converter
2026-01-05 — Auto-Detection & Package Maturity¶
Added¶
detect_provider()for automatic provider format auto-detectionconvert()convenience function for one-step format conversiondeveloperrole support in message validation- Comprehensive validation tests for
BaseConverter, Anthropic, Google GenAI, and OpenAI converters - Tool call and tool definition conversion tests
- pytest configuration and
pytest-covdependency - Competitive analysis document
Changed¶
- Package renamed from
llm-provider-convertertollm-rosetta - IR format usage standardized across all providers
- Message creation standardized using
Messageclass in examples - Test suite migrated from unittest to pytest
- Common logic extracted into shared utility modules
Fixed¶
- Standalone tool calls without current message context in OpenAI Responses converter
- Google GenAI Pydantic model handling reordered for tuple compatibility
- OpenAI content handling logic simplified for single text parts
2026-01-04 — Examples & Packaging¶
Added¶
pyproject.tomlfor package configuration- Multi-turn chat example with tool integration
- Anthropic handover in multi-turn chat example
- Google GenAI function calling in multi-turn chat example
Changed¶
- Utility functions moved from converters to IR types module
- OpenAI Chat converter code formatting improved
- Deprecated multi-provider query and weather tool modules removed
2025-12-24 — Initial Implementation¶
Added¶
- IR type system: intermediate representation types for messages, content parts, tools, configs, request/response
BaseConverterabstract class for LLM provider conversionAnthropicConverter: bidirectional Anthropic Messages API conversionOpenAIChatConverter: bidirectional OpenAI Chat Completions API conversionOpenAIResponsesConverter: bidirectional OpenAI Responses API conversionGoogleGenAIConverter: bidirectional Google GenAI SDK format conversion- Comprehensive test suites for all 4 converters
- Package initialization and exports
- Weather tool example with mock data
2025-12-09 — Research & Design¶
Added¶
- Initial project structure
- LLM provider message typing schemas documentation and comparison
- Provider messages IR design documentation
- MCP support comparison across providers (OpenAI, Anthropic, Google)
- Google GenAI Interactions API type analysis
- Multi-provider query example function
- OpenAI Responses API support in query examples