* feat(decisions): add the OpenAI Decisions spec types and the System One translation Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor(decisions): share one DecisionsModel config and require model and usage on responses Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor(decisions): rename the shared pydantic parent to DecisionsObjectBase Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * refactor(decisions): dispatch /v1/decisions through provider configs and the shared HTTP handler Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(decisions): map OpenRouter connection failures to APIConnectionError and drop explanatory docstrings Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * feat(decisions): switch /v1/decisions to the OpenAI Decisions schema Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(decisions): build the usage the OpenAI response schema now requires Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix(lens): ask signal questions through the OpenAI Decisions schema Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * feat(decisions): refuse safety_identifier for System One providers unless drop_params * feat(decisions): refuse safety_identifier on providers that cannot take it unless drop_params drops it * test(decisions): cover hosted_vllm in the safety_identifier and per-provider wire tests * fix(decisions): check a System One request's safety_identifier against the provider too * fix(decisions): drop a non-string safety_identifier under drop_params A malformed safety_identifier now follows the drop_params convention in both body shapes: it answers 400 without drop_params and is dropped before the provider call with it. A string identifier on OpenAI stays on the wire either way. * fix(decisions): let /v1/decisions drop a non-string safety_identifier under drop_params The route checked the whole body before routing, so a non-string safety_identifier answered 400 even when the deployment or the body set drop_params. The route now leaves that field to the Decisions call, which knows every drop_params source * refactor(decisions): return the unsupported safety_identifier as a value and raise it in _prepare_call --------- Co-authored-by: kerry <kerry@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com> |
||
|---|---|---|
| .. | ||
| chat_completions | ||
| decisions | ||
| messages | ||
| responses | ||
| __init__.py | ||
| AGENTS.md | ||
| case.py | ||
| conftest.py | ||
| README.md | ||
| runner.py | ||
Translation tests
These tests check one request through the proxy as literals on a TranslationTestCase: the litellm_endpoint and litellm_request the test sends, the expected_provider_endpoint, expected_provider_headers and expected_provider_request the fake provider must receive, the mock_provider_response it answers with, and the expected_litellm_status_code and expected_litellm_response the test must get back. The runner compares the provider request body, every provider header other than transport headers, and the LiteLLM response body in full, so an added, removed, renamed or moved field fails. Folders follow the client endpoint and then the feature, for example translation/messages/reasoning/. Each endpoint keeps one complete base case per model in <endpoint>/bases/<provider>.py, named <MODEL>_TEST_CASE (for example CLAUDE_SONNET_4_6_TEST_CASE), which <endpoint>/basic/ runs on its own. A feature case is <MODEL>_<SCENARIO>_TEST_CASE = dataclasses.replace(<MODEL>_TEST_CASE, ...), imports the base under its own name rather than as BASE, and lists only the fields it changes
Some response fields are generated by LiteLLM on every run, such as a chat completion's id and created or a response's id, created_at and output message id. A case writes those as unittest.mock.ANY, so the field must still be present but any value passes. Use ANY only for a value LiteLLM generates, never for one copied from the provider reply
Deployments used by translation tests are shared by the whole suite and declared in proxy_config.yaml under model_list, with model_name equal to the litellm model string, api_base: http://127.0.0.1:8191 and a synthetic key. A case names the deployment literally in its client request. The fake provider behind them is the provider fixture: one wire_server on port 8191 inside the pytest process, started the first time a test asks for it. A test queues its replies with provider.expect(...) and reads what the proxy sent with provider.received(). Tests that use it run one at a time. After each test the fixture fails if a queued reply was never requested or a received request was never read, and before each test it fails if a request arrived in between, naming the previous test. It refuses to start under pytest-xdist, so the mcp and cost groups cannot use it. Client requests carry "cache": {"no-cache": True} because the proxy caches responses in Redis
Provider responses in translation cases are captured once from the real provider and stored verbatim. First run the new case against the fake provider; the body the proxy sends is the case's expected_provider_request. Send that exact body to the real provider endpoint with a key from the 1Password Shared vault (/qa-keys), and only store the case when the provider answers 2xx. Keep only the response body and drop every response header, since headers carry account identifiers such as the organization id and rate limits. Never print or save the request headers you sent. Before committing, check the body contains no key and no account identifier, such as an organization id, an AWS account id inside an ARN, a GCP project id or an Azure resource name, and that the prompt is synthetic. Paste the body as the case's mock_provider_response without shortening ids, token counts or signatures, and move long opaque values such as thinking signatures into module-level constants used by both mock_provider_response and expected_litellm_response. Do not commit the script used for the capture