This design adds two new usage-log fields:
chat_id: extracted from the incoming request bodyupstream_id: extracted from the upstream response headersThe existing logs.request_id field keeps its current meaning and continues to represent the internal new-api request ID.
The extraction logic is centralized and writes normalized values into relay context first, then both consume logs and error logs read from the same context when persisting to logs.
The current system has only one stable request identifier in usage logs: the internal logs.request_id.
For reconciliation work, that is not enough:
chat_idToday these values are not recorded consistently in consume logs. Error logs have partial upstream request-id support, but consume logs do not have a unified path.
chat_id into usage logslogs.request_idThe logs table will have three distinct request identifiers:
request_id: internal new-api request ID, already existingchat_id: business ID extracted from the incoming request bodyupstream_id: upstream request ID extracted from response headersField semantics must not overlap.
Add two nullable string columns to logs:
chat_idupstream_idConstraints:
Reasoning:
When the request body is parsed into the relay request object, the system performs a best-effort extraction of a top-level chat_id.
Behavior:
chat_idchat_id is missingRelayInfo.ChatIDWhen an upstream http.Response is received, before body consumption begins, the system performs a best-effort extraction of the upstream request ID and stores it in RelayInfo.UpstreamID.
First-version default rule chain:
x-request-idrequest-idThis rule chain is intentionally generic. The extractor should be implemented as a reusable module with:
Consume logs and error logs do not parse request bodies or response headers directly.
They only read:
request_idRelayInfo.ChatIDRelayInfo.UpstreamIDand persist them into logs.
This keeps extraction and logging decoupled.
chat_idx-request-id / request-idrequest_id, chat_id, upstream_idchat_id before relay startsupstream_id from headersrequest_id, chat_id, upstream_idNo per-chunk ID parsing is required in the first version.
Retries must not collapse multiple upstream attempts into one combined ID set.
Rules:
upstream_idupstream_idThis preserves clean reconciliation semantics for billed requests.
The design must not materially affect relay throughput or stream latency.
Allowed work:
chat_id extraction while request parsing already happensForbidden first-version approaches:
Expected impact:
chat_id extraction: negligible, because request parsing already occursupstream_id extraction: negligible, because headers are already availableThe feature is best-effort only.
Rules:
chat_id does not fail the requestFirst version should support:
chat_idupstream_idThe first version does not need both fields as default main table columns.
chat_id, upstream returns x-request-id, consume log stores bothchat_id, upstream stream response includes x-request-id, consume log stores bothupstream_idx-request-id is absent and request-id exists, upstream_id is still storedlogs.request_id behaviorRecommended implementation shape:
relay/common.RelayInfo with ChatID and UpstreamIDchat_idmodel.Log and log persistence functions to carry chat_id and upstream_idx-request-idchat_idMitigation:
Approved design decisions:
logs.request_id unchanged as the internal request IDlogs.chat_idlogs.upstream_idchat_id from the request bodyupstream_id from response headers, defaulting to x-request-id and falling back to request-idupstream_id in consume logs during retry scenarios