Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 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
c381516
fix(api): treat tool/json_schema strict null as omit; require tools f…
seonghobae Aug 16, 2026
5dace0f
fix(api): treat tool.function description/parameters null as omit
seonghobae Aug 16, 2026
55d7a45
fix(api): cap tool.function.description at 1024 characters fail-closed
seonghobae Aug 16, 2026
4f84700
fix(api): treat chat message name empty/whitespace as omit
seonghobae Aug 16, 2026
d8aa84c
fix(api): treat top_logprobs empty-string and tool_calls arguments nu…
seonghobae Aug 16, 2026
95346e7
fix(api): treat Responses instructions empty/whitespace as omit
seonghobae Aug 16, 2026
85617de
fix(api): pop blank Responses instructions before passthrough
cursoragent Aug 16, 2026
2f420a9
fix(api): accept official Responses text.format type=text
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
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# 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

- `/v1/responses` now accepts the official OpenAI SDK default
`text: {format: {type: "text"}}` and forwards it. Other non-empty `text`
objects still return `invalid_text`. Send `json_object` / `json_schema` via
`response_format` until `text.format` structured types land. Do not retry an
unsupported `text` object.
- `/v1/responses` now treats JSON `null`, empty, and whitespace `instructions` as
**omit-real**: the key is removed before provider passthrough so OpenAI SDK
optional defaults do not become a blank upstream system prompt. Non-string and
>32000-character values still return `invalid_instructions`. Send a non-empty
string when you want a system prompt; do not retry a blank payload.

## [0.1.0] - 2026-08-16

### Added

- OpenAI-compatible chat, Responses, embeddings, and batch routing surfaces with
Fugu / TRINITY / Conductor orchestration, KV credentials, and admin evidence.
52 changes: 52 additions & 0 deletions contextual_orchestrator/api_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,58 @@
},
}
},
"/v1/responses": {
"post": {
"operationId": "create_model_response",
"summary": (
"OpenAI Responses passthrough. Omit instructions, or send null/empty/"
"whitespace — the gateway deletes the key before upstream."
),
"security": [{"inference_bearer_auth": []}],
"requestBody": {
"required": True,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["input"],
"properties": {
"model": {"type": "string"},
"input": {
"oneOf": [
{"type": "string"},
{"type": "array"},
]
},
"instructions": {
"type": ["string", "null"],
"description": (
"Optional system-style prompt. JSON null, empty, "
"or whitespace is omit-real (key removed). "
"Non-strings and values over 32000 characters "
"return invalid_instructions."
),
},
"text": {
"type": ["object", "null"],
"description": (
"Official SDK default {format: {type: text}} is "
"accepted and forwarded. Other non-empty text "
"objects return invalid_text; use response_format "
"for json_object / json_schema."
),
},
},
}
}
},
},
"responses": {
"200": {"description": "Provider Responses object from the selected pool agent"},
"400": {"description": "invalid_instructions, invalid_text, invalid_input, or other fail-closed field"},
},
}
},
"/api/v1/access_reports/{workflow_run_id}": {
"get": {
"operationId": "get_access_report",
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