Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
743c6e9
fix(api): treat message audio/function_call null-empty as omit; fail-…
seonghobae Aug 16, 2026
e3a6e0e
fix(api): treat message weight 0/1/null as omit-equivalent; fail-clos…
seonghobae Aug 16, 2026
4a95b9a
fix(api): fail-closed on unknown chat message fields and legacy funct…
seonghobae Aug 16, 2026
82038d3
fix(api): treat message prefix null/false as omit; fail-closed on true
seonghobae Aug 16, 2026
1a196b0
fix(api): treat chat max_tool_calls null/empty as omit; fail-closed o…
seonghobae Aug 16, 2026
3a0d35e
fix(api): treat Completions max_tool_calls null/empty as omit; fail-c…
seonghobae Aug 16, 2026
9d10fa9
fix(api): treat stream_options null flags as omit-equivalent no-ops
seonghobae Aug 16, 2026
b108251
fix(api): fail-closed unknown stream_options keys and tools-path mess…
cursoragent Aug 16, 2026
856adb8
fix(api): fail-closed stream, model, and sampling on tools passthrough
cursoragent Aug 16, 2026
169a382
fix(api): fail-closed empty messages and spend keys on tools passthrough
cursoragent Aug 16, 2026
b950724
fix(api): fail-closed batch routing hints on tools passthrough
cursoragent Aug 16, 2026
dbfa0e5
docs: tell buyers to omit batch routing on tool-calling requests
cursoragent Aug 16, 2026
ffad9cb
fix(api): fail-closed seed/stop/n/logprobs on tools passthrough
cursoragent Aug 16, 2026
18e6263
fix(api): apply request temperature on streamed route completions
cursoragent Aug 16, 2026
bc73dd2
fix(api): pop null tool.function description/parameters/strict before…
cursoragent Aug 16, 2026
7e9a339
docs: record passthrough knob and stream temperature fixes in CHANGELOG
cursoragent Aug 16, 2026
37f1224
fix(api): fail-closed mode and orchestration-trace on tools passthrough
cursoragent Aug 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Changelog

All notable changes to this project are documented in this file.

The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Fixed

- Treat official-SDK JSON `null` on optional `tools[].function.description`,
`parameters`, and `strict` as omit-real: the keys are popped before
`proxy_completion` so upstream providers see an omitted field, not a null
schema. Non-null wrong types still fail closed with named `invalid_tools`.
Next action: send those fields only when you have a real string, JSON Schema
object, or boolean; SDK defaults of `null` are safe.
- Fail closed on tools passthrough for `seed`, `stop`, `n>1`, `logprobs`,
`logit_bias`, and out-of-range penalties — the same named errors as the
orchestration path. Next action: omit those knobs on tool-calling requests.
- Fail closed on tools passthrough for invalid `mode` / `orchestration`,
`mode=conduct`, non-boolean `include_orchestration_trace`, and
`include_orchestration_trace=true`. This path has no Conductor workflow or
trusted-trace plane. Next action: omit `mode` or send `auto`/`route`; omit
the trace flag or set it `false`.
- Apply the request `temperature` on streamed route completions instead of
silently using `0.2`. Next action: send the temperature you want; streaming
no longer changes the sampling policy.

### References

- OpenAI. (2024). *Create chat completion*. OpenAI API reference.
https://platform.openai.com/docs/api-reference/chat/create
- Bray, T. (Ed.). (2017). *The JavaScript Object Notation (JSON) data
interchange format* (RFC 8259). Internet Engineering Task Force.
https://doi.org/10.17487/RFC8259
- Nielsen, S., Cetin, E., Schwendeman, P., Sun, Q., Xu, J., & Tang, Y.
(2025). *Learning to orchestrate agents in natural language with the
Conductor*. arXiv. https://doi.org/10.48550/arXiv.2512.04388
- Xu, J., Sun, Q., Schwendeman, P., Nielsen, S., Cetin, E., & Tang, Y.
(2025). *Trinity: An evolved LLM coordinator*. arXiv.
https://doi.org/10.48550/arXiv.2512.04695
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Non-mock providers must use `https://` URLs and a **resolvable KV credential**

One public interface:

- `/v1/chat/completions` accepts normal chat messages, and `"stream": true` returns an OpenAI-compatible `text/event-stream` of `chat.completion.chunk` deltas terminated by `data: [DONE]`. In **route** mode the worker's tokens are streamed live as they arrive from the provider (real token streaming); in **conduct** mode the multi-step answer is produced then framed as deltas (a workflow can't honestly token-stream a synthesizer that hasn't run yet).
- `/v1/chat/completions` accepts normal chat messages, and `"stream": true` returns an OpenAI-compatible `text/event-stream` of `chat.completion.chunk` deltas terminated by `data: [DONE]`. In **route** mode the worker's tokens are streamed live as they arrive from the provider (real token streaming); in **conduct** mode the multi-step answer is produced then framed as deltas (a workflow can't honestly token-stream a synthesizer that hasn't run yet). `/v1/completions`, `/v1/responses`, and `/v1/embeddings` are the matching compatibility surfaces. Send `stream_options.include_usage` / `include_obfuscation` as omitted, `null`, or `false`; `true` and unknown keys fail closed. Tool-calling bodies must send a non-empty `messages` array of objects — empty, omitted, or non-object entries fail closed before passthrough. Omit `routing.channel=batch` and `latency_tolerant=true` on tool-calling requests; this gateway has no batch job plane on passthrough. Omit `seed`, `stop`, `n>1`, `logprobs`, `mode=conduct`, and `include_orchestration_trace=true` on tool-calling requests — those knobs are not applied on passthrough and fail closed instead of billing a silent drop. Invalid `mode` values also fail closed. Use `mode=auto` or omit mode; omit the trace flag or set it `false`. Streamed route requests apply the `temperature` you send; they do not silently fall back to `0.2`. Optional `tools[].function.description`, `parameters`, and `strict` sent as JSON `null` are popped before the provider hop so SDK defaults stay omit-equivalent.
- `TaskOrchestrator.complete()` decides whether to route to one worker or run a short workflow.
- `TaskOrchestrator.compare_to_baseline(prompts, mode)` (CLI `--eval PROMPT...`) measures the orchestration engine against a single-worker baseline — per-prompt and aggregate latency plus a structural coverage delta (contributing steps + verifier-pass presence). It is a measured tradeoff report, not a human-quality claim.
- Responses include orchestration mode metadata, and trusted callers can request the full trace for audit.
Expand Down
39 changes: 29 additions & 10 deletions contextual_orchestrator/cost_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ class UsageRecord:

def as_dict(self) -> Dict[str, Any]:
"""Flatten the record (attribution inlined) for JSON + SQL storage."""
# Execution identity is evidence of what ran — never a client-chosen tag.
# Account/service/team/group/company remain descriptive attribution.
row = {
"usage_record_id": self.usage_record_id,
"created_at": self.created_at,
Expand Down Expand Up @@ -583,12 +585,12 @@ def _seed_dimension_catalog(self) -> None:
ph = self._placeholder()
cur = self._conn.cursor()
for order, (name, label, _column) in enumerate(ATTRIBUTION_DIMENSION_CATALOG):
cur.execute(
cur.execute( # nosemgrep -- sqlalchemy-execute-raw-query FP: only the DB-API placeholder char is interpolated; the value is bound.
f"SELECT 1 FROM cost_attribution_dimensions WHERE dimension_name = {ph}", # nosec B608 - ph is a DB-API placeholder.
(name,),
)
if cur.fetchone() is None:
cur.execute(
cur.execute( # nosemgrep -- sqlalchemy-execute-raw-query FP: only DB-API placeholder chars are interpolated; values are bound.
"INSERT INTO cost_attribution_dimensions "
f"(dimension_name, dimension_label, dimension_order) VALUES ({ph}, {ph}, {ph})", # nosec B608 - ph is a DB-API placeholder.
(name, label, order),
Expand All @@ -602,7 +604,7 @@ def append(self, record: UsageRecord) -> None:
placeholders = ", ".join(ph for _ in _USAGE_COLUMNS)
columns = ", ".join(_USAGE_COLUMNS)
cur = self._conn.cursor()
cur.execute(
cur.execute( # nosemgrep -- sqlalchemy-execute-raw-query FP: columns are the fixed _USAGE_COLUMNS constant; values are bound.
f"INSERT INTO llm_usage_records ({columns}) VALUES ({placeholders})", # nosec B608 - columns are fixed _USAGE_COLUMNS.
tuple(row.get(column) for column in _USAGE_COLUMNS),
)
Expand All @@ -622,7 +624,7 @@ def query(self, start: Optional[int] = None, end: Optional[int] = None) -> List[
where = f" WHERE {' AND '.join(clauses)}" if clauses else ""
columns = ", ".join(_USAGE_COLUMNS)
cur = self._conn.cursor()
cur.execute(f"SELECT {columns} FROM llm_usage_records{where}", tuple(params)) # nosec B608 - columns and clauses are fixed.
cur.execute(f"SELECT {columns} FROM llm_usage_records{where}", tuple(params)) # nosec B608 - columns and clauses are fixed. # nosemgrep -- sqlalchemy-execute-raw-query FP: fixed columns and clause templates; all values are bound.
return [dict(zip(_USAGE_COLUMNS, values)) for values in cur.fetchall()]


Expand Down Expand Up @@ -681,14 +683,31 @@ def record_usage(
) -> UsageRecord:
"""Compute cost, build a :class:`UsageRecord`, persist it, and return it."""
if isinstance(attribution, dict) or attribution is None:
dims = AttributionDimensions.from_mapping(attribution)
# Strip caller-controlled execution identity before mapping so a
# client cannot spoof model/provider rollups (buyer-bill honesty).
if isinstance(attribution, dict):
cleaned = {
key: value
for key, value in attribution.items()
if key not in {"model_name", "provider", "upstream_api"}
}
else:
cleaned = None
dims = AttributionDimensions.from_mapping(cleaned)
else:
dims = attribution
# Keep the model_name dimension aligned with the served model unless the
# caller pinned it explicitly, and default the provider dimension too.
if dims.model_name == UNATTRIBUTED and model:
dims = AttributionDimensions(
account=attribution.account,
service=attribution.service,
upstream_api=UNATTRIBUTED,
model_name=UNATTRIBUTED,
team=attribution.team,
group=attribution.group,
company=attribution.company,
)
# Execution identity always wins — descriptive dimensions stay as-is.
if model:
dims.model_name = model
if dims.upstream_api == UNATTRIBUTED and provider:
if provider:
dims.upstream_api = provider

cost_amount, currency = self.price_book.compute_cost(
Expand Down
Loading
Loading