feat(api): fail-closed image_url rasters and persist message_image_unit - #676
Closed
cursor[bot] wants to merge 22 commits into
Closed
feat(api): fail-closed image_url rasters and persist message_image_unit#676cursor[bot] wants to merge 22 commits into
cursor[bot] wants to merge 22 commits into
Conversation
…closed otherwise Chat history: message-level audio and legacy function_call are null/empty omit no-ops; non-empty fail closed with named errors (including tools passthrough). Tip substrate from #577 assistant refusal/annotations honesty. Local full unit: 940 passed.
…ed otherwise OpenAI fine-tune style message weight is not applied on this gateway. Accept null/0/1 as honest no-ops; reject other types and values with invalid_message_weight. Tip substrate from #578. Local full unit: 943 passed.
…ion role Reject unsupported message keys with named unknown_message_fields (not silent strip or tools-passthrough smuggle). Reject legacy function role with invalid_message_role migration to tool. Tip substrate from #579. Local full unit: 947 passed.
OpenAI partial-assistant prefix flag is not applied on this gateway. null/false are honest no-ops; true and non-booleans fail closed with invalid_message_prefix. Tip substrate from #580. Local full unit: 950 passed.
…therwise Named invalid_max_tool_calls on /v1/chat/completions instead of opaque unknown_fields. Aligns with Responses max_tool_calls honesty; gateway has no multi-step tool loop.
…losed otherwise Legacy /v1/completions treated max_tool_calls as unknown_fields. Accept the key for named invalid_max_tool_calls (null/empty/whitespace omit-equivalent), matching chat/Responses honesty so SDKs get a clear migration path.
SDK clients often send include_usage/include_obfuscation as JSON null. Drop null flag values before validation so null (and null+false mixes) match omit / all-false no-ops on chat, Completions, and Responses. True flags remain fail-closed with invalid_stream_options.
…age honesty Null include_usage/include_obfuscation stay omit-equivalent, but unknown stream_options keys no longer become no-ops just because their value is null. Weight, prefix, refusal, annotations, developer role, empty user/system content, and participant name now use the same named errors on the tools passthrough path as on orchestration. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Hoist stream, required model, stream_options, and temperature/top_p range checks before proxy_completion so a tools or response_format body cannot return a billed JSON completion when the SDK asked for SSE, or silently pick a pool model when model is omitted. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Call the orchestration message, max_tokens, attribution, and routing validators before proxy_completion so a tools or response_format body cannot bill a completion with no prompt, crash on a non-object entry, or silently drop unknown spend/routing keys. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Passthrough has no batch job plane. Reject routing.channel=batch and latency_tolerant=true before proxy_completion so a tool-calling body cannot bill a silent sync completion. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Hoist the orchestration request-knob validators before proxy_completion so a tools or response_format body cannot bill a completion that silently drops seed, stop, n>1, logprobs, logit_bias, or out-of-range penalties. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
stream_chat defaulted to 0.2 even after the HTTP path wrote default_temperature from the request. A streamed invoice summary at temperature=0.8 no longer silently falls back to 0.2. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
… proxy SDK optional defaults serialize omitted fields as JSON null. Accepting those keys without popping them is not omit-equivalent: proxy_completion forwards the body and several providers reject a null JSON Schema object. Pop the keys in place, keep non-null wrong types on invalid_tools, and assert omit-real via mock echo on chat and Responses. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Stop reading CONTEXTUAL_ORCHESTRATOR_ALLOWED_PROVIDER_HOSTS in ModelClient._validate_provider. The allowlist lives in provider_egress.allowed_provider_hosts; env is bootstrap transport via seed_provider_egress_from_environ only. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Prove a second seed() after an env edit keeps the first KV set, that _validate_provider ignores env-only allowlists, and that a detached get_config_store() write is not the request-time source. Hold the runtime lock across seed read+write and fuzz the host CSV parser. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Irreversible email [REDACTED] on opt-in traces made invoice and HR tickets unworkable. Credential shapes stay redacted. Access control plus audit is the PII control (NIST SP 800-122; SP 800-53 AC-3/AU-2). Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
CONTEXTUAL_ORCHESTRATOR_TOKEN and the split admin/inference vars are bootstrap transport via seed_server_auth_from_environ. Explicit --auth-token still wins. A later env edit does not change who can call the API. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
…me KV CONTEXTUAL_ORCHESTRATOR_STATE_DB / _AGENTS_DB / _CLEARFOLIO_URL / _PROVIDER_CA_BUNDLE are bootstrap transport via seed_serve_runtime_from_environ. Explicit CLI flags still win. A later env edit does not retarget persistence, the document viewer, or provider TLS. Buyer next action: pass --state-db / --agents-db / --clearfolio-url / --provider-ca-bundle, or start once with the matching env vars so the serve_runtime KV can copy them, then open the KV sqlite path or Clearfolio URL. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
HTML, javascript, and truncated PNG data URIs no longer bill a vision hop. A real invoice-stamp PNG is accepted. Sqlite keeps part_index beside the neighbor invoice line so operators can reopen the figure after restart. Buyer next action: send a complete PNG/JPEG data URI or https receipt URL next to the invoice line, then call list_message_image_units. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
seonghobae
marked this pull request as ready for review
August 16, 2026 17:06
Author
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
seonghobae
enabled auto-merge (squash)
August 16, 2026 17:06
auto-merge was automatically disabled
August 16, 2026 18:22
Pull request was closed
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Unique slice on top of #637
5358632: invoice/receipt photos in OpenAIimage_urlparts no longer bill a vision hop when the payload is HTML,javascript:, or a truncated PNG. A real 1×1 PNG stamp is accepted. Sqlite persists a 3NFmessage_image_unitwith the originalpart_indexbeside neighboring invoice text so operators can reopen the figure after restart.Buyer next action: send a complete PNG/JPEG data URI or
https://…/receipt.pngnext to the invoice line, then calllist_message_image_unitsand open the unit whoseneighbor_textcontains the invoice id.Serve-runtime landing remains #637. Do not treat this PR as a second serve-runtime path vehicle. Do not merge #645/#656 in parallel for that slice. Do not fold #643/#652 meaning-unit embeddings or #631/#650 reasoning-effort into this tip.
Test plan
python3 tests/test_message_image_units.py(HTML/JS/truncated PNG fail closed withpart_index=1; real stamp 200; sqlite restart finds INV-4419 at part_index 1)python3 tests/test_multimodal_message_content_http_honesty.pypython3 tests/test_multimodal_content_parts_shape_http_honesty.pypython3 tests/test_persistence.pytest_serve_runtime_kv.pytest_conventions.pytest_api_contract.pytest_self_check.pyDocs
docs/papers/README.mdAPA 7th: Xu et al. (2020) LayoutLM; Masinter (1998) RFC 2397; Boutell (1997) RFC 2083.docs/library_research.mdPonytail: stdlib base64/urlparse; skip Pillow and live OCR in this slice.docs/database_design.sqlmessage_image_unit(3NF, two-word names, retention).Independent non-author APPROVE + Full unit/Semgrep still required.