Conversation
7fbb91f to
64f4bad
Compare
8d7eba7 to
9d2ccf3
Compare
64f4bad to
e569a99
Compare
9d2ccf3 to
bd8d790
Compare
| /// Known request fields for OpenAI Responses API. | ||
| /// These are fields extracted into UniversalRequest/UniversalParams. | ||
| /// Fields not in this list go into `extras` for passthrough. | ||
| const RESPONSES_KNOWN_KEYS: &[&str] = &[ |
There was a problem hiding this comment.
i move responses to responses_adapter.rs
cf02e72 to
349a05d
Compare
crates/coverage-report/src/runner.rs
Outdated
| target_adapter.display_name(), | ||
| test_case, | ||
| ); | ||
| let roundtrip_result = compare_values( |
There was a problem hiding this comment.
i tightened the runner to be more accurate, basically we now compare:
source -> universal
with
source -> universal -> target -> universal
we basically deserialize universal -> JSON and do diffing
a75144e to
4e0703c
Compare
f2ba481 to
c8a3e48
Compare
4e0703c to
5aa114b
Compare
c8a3e48 to
f2ba481
Compare
4e0703c to
561180b
Compare
f2ba481 to
a38f078
Compare
561180b to
c94e076
Compare
| /// Tool selection strategy (varies by provider) | ||
| pub tool_choice: Option<Value>, | ||
| /// Number of top logprobs to return (0-20) | ||
| pub top_logprobs: Option<i64>, |
There was a problem hiding this comment.
nit: if it's 1-20 can it be a smaller integer type like i8?
There was a problem hiding this comment.
the openai generated type equivalent is i64 -> https://github.com/braintrustdata/lingua/blob/main/crates/lingua/src/providers/openai/generated.rs#L69
| // === Metadata and identification === | ||
| /// Request metadata (user tracking, experiment tags, etc.) | ||
| pub metadata: Option<Value>, |
There was a problem hiding this comment.
responses/chatcompletions have it as:
metadata
map
Optional
Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard.
Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters.
while anthrpoic has metadata as an object with one field user_id.
https://platform.openai.com/docs/api-reference/responses/create#responses_create-metadata
https://platform.claude.com/docs/en/api/messages
There was a problem hiding this comment.
ahh ok. mind linking these in the comment
| /// Example: OpenAI Chat extras stay in `provider_extras[ProviderFormat::OpenAI]` | ||
| /// and are only merged back when converting to OpenAI Chat, not to Anthropic. | ||
| #[serde(skip)] | ||
| pub provider_extras: HashMap<ProviderFormat, Map<String, Value>>, |
There was a problem hiding this comment.
it's slightly weird that this is not nested in params , at least to me. What as the rationale behind that?
There was a problem hiding this comment.
ankrgyl
left a comment
There was a problem hiding this comment.
Looks pretty good straightforward to me
- It's a little out of date, but it would be useful to write some typescript examples (eg in
examples/typescript/index.ts) or even some rust examples that show the ergonomics of using parameters, so we can double check the format - for parameters, i think it would be useful to write a "fuzz" style tester that for each provider, generates random values with respect to the openapi spec, and then roundtrips through
UniversalParams(there is less entropy in parameters than raw requests, but this might just be generally useful) - Is there a creative way we can port the test cases we have in the proxy/ repo? We have had a bunch of historical challenges with translating reasoning for example that is well captured in those tests.
b2a3e48 to
0ffbf84
Compare
just to clarify, did you address these too? |
| /// - ratio >= 0.65: high | ||
| pub fn budget_to_effort(budget: i64, max_tokens: Option<i64>) -> ReasoningEffort { | ||
| let max = max_tokens.unwrap_or(DEFAULT_MAX_TOKENS); | ||
| let ratio = budget as f64 / max as f64; |
There was a problem hiding this comment.
Are we enforcing that max is a strictly positive integer?
There was a problem hiding this comment.
good catch! adddedtests
| MissingRequiredField { field: String }, | ||
|
|
||
| #[error("Invalid role: {role}")] | ||
| InvalidRole { role: String }, |
There was a problem hiding this comment.
crates/lingua/src/universal/tools.rs
Outdated
| ); | ||
|
|
||
| // Responses API function tools have strict: false by default | ||
| obj.insert("strict".into(), Value::Bool(false)); |
There was a problem hiding this comment.
what if a request had strict: true ? wouldn't that override it ?
There was a problem hiding this comment.
chat completions don't have strict;true for tools. google and anthropic does though so i'll preemptively add support now.
i realized our anthropic spec is out of date
| "completion_tokens": completion, | ||
| "total_tokens": prompt + completion | ||
| }); | ||
| let obj_map = obj.as_object_mut().unwrap(); |
There was a problem hiding this comment.
should we avoid using unwrap() and use expect() instead to give more context in case of crash? I think there are a few places in your PR where you use unwrap().
| pub const EFFORT_HIGH_MULTIPLIER: f64 = 0.75; | ||
|
|
||
| /// Threshold below which budget is considered "low" effort | ||
| pub const EFFORT_LOW_THRESHOLD: f64 = 0.35; |
There was a problem hiding this comment.
are those thresholds documented anywhere? or is it just something you came up with (which would be fine)
There was a problem hiding this comment.
these are copied from the old proxy as a starting point.
remh
left a comment
There was a problem hiding this comment.
A few comments that need to be addressed but otherwise it looks good to me.
0ffbf84 to
80528b1
Compare
there are still some proxy failures but i got most tests passing. |
52805d2 to
af4a595
Compare
| @@ -0,0 +1,915 @@ | |||
| import OpenAI from "openai"; | |||
There was a problem hiding this comment.
all the cases i ported from proxy that "fit" into this. some of the proxy cases intercepted calls so that doesn't really work yet. I have a subsequent PR with a more "intercept"-like approach. https://app.graphite.com/github/pr/braintrustdata/lingua/72
there are still some proxy failures but i got most tests passing.
proxyAnthropicAudioError - audio PR
proxyOpenAIO3MiniReasoning - openai capabilitiescapabilities
proxyGoogleReasoning - google next PR
proxyGoogleAudioSupport - google next PR
proxyGoogleVideoSupport - google next PR
proxyAzureParamFiltering - capabilities
proxyOpenAIO3MiniStreamingReasoning - openai capabilities
proxyOpenAIPdfUrlConversion - I'm not sure how to do this, we would have to make a network call and then encode(?)

Summary
This PR adds cross-provider compatability for chat completions, responses, anthropic.
See -> https://github.com/braintrustdata/lingua/actions/runs/21328545339
Parameter mappings
reasoning_effortreasoning.effortthinking.budget_tokensresponse_format.json_schematext.formatoutput_formattool_choicetool_choicetool_choice+disable_parallel_tool_usemax_tokens/max_completion_tokensmax_output_tokensmax_tokensTesting
For each provider pair (A → B) across Chat Completions / Responses / Anthropic, we validate the deserialized Universal payload:
Universal of source payload
U₁ = A payload → UniversalTranslate across providers and re-canonicalize
U₂ = (A payload → Universal → B payload) → UniversalDiff the canonical forms
U₁vsU₂Enforce in CI
Expected differences
expected_differences.json.