Skip to content

Commit 58726f0

Browse files
committed
AIX: OpenRouter: provider infra label
1 parent 85f796f commit 58726f0

8 files changed

Lines changed: 39 additions & 0 deletions

File tree

src/common/stores/chat/chat.message.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ export type DMessageGenerator = ({
141141
},
142142
}) & {
143143
metrics?: DMetricsChatGenerate_Md; // medium-sized metrics stored in the message
144+
providerInfraLabel?: string; // upstream provider that served the request (e.g., OpenRouter provider routing)
144145
upstreamHandle?: {
145146
uht: 'vnd.oai.responses',
146147
responseId: string,
@@ -244,6 +245,7 @@ export function duplicateDMessageGenerator(generator: Readonly<DMessageGenerator
244245
name: generator.name,
245246
// ...(generator.xeOpCode ? { xeOpCode: generator.xeOpCode } : {}),
246247
...(generator.metrics ? { metrics: { ...generator.metrics } } : {}),
248+
...(generator.providerInfraLabel ? { providerInfraLabel: generator.providerInfraLabel } : {}),
247249
...(generator.upstreamHandle ? { upstreamHandle: { ...generator.upstreamHandle } } : {}),
248250
...(generator.tokenStopReason ? { tokenStopReason: generator.tokenStopReason } : {}),
249251
};
@@ -253,6 +255,7 @@ export function duplicateDMessageGenerator(generator: Readonly<DMessageGenerator
253255
name: generator.name,
254256
aix: { ...generator.aix },
255257
...(generator.metrics ? { metrics: { ...generator.metrics } } : {}),
258+
...(generator.providerInfraLabel ? { providerInfraLabel: generator.providerInfraLabel } : {}),
256259
...(generator.upstreamHandle ? { upstreamHandle: { ...generator.upstreamHandle } } : {}),
257260
...(generator.tokenStopReason ? { tokenStopReason: generator.tokenStopReason } : {}),
258261
};

src/common/util/dMessageUtils.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ export function useMessageAvatarLabel(
258258
tooltip: complexity === 'minimal' ? null : (
259259
<Box sx={tooltipSx}>
260260
{VendorIcon ? <Box sx={tooltipIconContainerSx}><VendorIcon />{generator.name}</Box> : <div>{generator.name}</div>}
261+
{generator.providerInfraLabel && <div>{vendorId} -&gt; via &lsquo;{generator.providerInfraLabel}&rsquo;</div>}
261262
{(modelId && complexity === 'extra') && <div>{modelId}</div>}
262263
{metrics && <div>{metrics}</div>}
263264
{stopReason && <div>{stopReason}</div>}

src/modules/aix/client/ContentReassembler.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ export class ContentReassembler {
302302
case 'set-model':
303303
this.onModelName(op);
304304
break;
305+
case 'set-provider-infra':
306+
this.onProviderInfra(op);
307+
break;
305308
case 'set-upstream-handle':
306309
this.onResponseHandle(op);
307310
break;
@@ -791,6 +794,10 @@ export class ContentReassembler {
791794
this.accumulator.genModelName = name;
792795
}
793796

797+
private onProviderInfra({ label }: Extract<AixWire_Particles.ChatGenerateOp, { cg: 'set-provider-infra' }>): void {
798+
this.accumulator.genProviderInfraLabel = label;
799+
}
800+
794801
private onResponseHandle({ handle }: Extract<AixWire_Particles.ChatGenerateOp, { cg: 'set-upstream-handle' }>): void {
795802
// validate the handle
796803
if (handle?.uht !== 'vnd.oai.responses' || !handle?.responseId || handle?.expiresAt === undefined) {

src/modules/aix/client/aix.client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,8 @@ function _llToDMessageGuts(src: AixChatGenerateContent_LL, dest: AixChatGenerate
537537
dest.generator.metrics = metricsChatGenerateLgToMd(src.genMetricsLg); // reduce the size to store in DMessage
538538
if (src.genModelName)
539539
dest.generator.name = src.genModelName;
540+
if (src.genProviderInfraLabel)
541+
dest.generator.providerInfraLabel = src.genProviderInfraLabel;
540542
if (src.genUpstreamHandle)
541543
dest.generator.upstreamHandle = src.genUpstreamHandle;
542544
if (src.legacyGenTokenStopReason)
@@ -577,6 +579,7 @@ export interface AixChatGenerateContent_LL {
577579
// pieces of generator
578580
genMetricsLg?: DMetricsChatGenerate_Lg;
579581
genModelName?: string;
582+
genProviderInfraLabel?: string;
580583
genUpstreamHandle?: DMessageGenerator['upstreamHandle'];
581584
legacyGenTokenStopReason?: DMessageGenerator['tokenStopReason'];
582585
}

src/modules/aix/server/api/aix.wiretypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,7 @@ export namespace AixWire_Particles {
644644
| { cg: 'retry-reset', rScope: 'srv-dispatch' | 'srv-op' | 'cli-ll', rShallClear: boolean, reason: string, attempt: number, maxAttempts: number, delayMs: number, causeHttp?: number, causeConn?: string }
645645
| { cg: 'set-metrics', metrics: CGSelectMetrics }
646646
| { cg: 'set-model', name: string }
647+
| { cg: 'set-provider-infra', label: string }
647648
| { cg: 'set-upstream-handle', handle: { uht: 'vnd.oai.responses', responseId: string, expiresAt: number | null } }
648649
| { cg: '_debugDispatchRequest', security: 'dev-env', dispatchRequest: { url: string, headers: string, body: string, bodySize: number } } // may generalize this in the future
649650
| { cg: '_debugProfiler', measurements: Record<string, number | string>[] };

src/modules/aix/server/dispatch/chatGenerate/ChatGenerateTransmitter.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,14 @@ export class ChatGenerateTransmitter implements IParticleTransmitter {
494494
this._queueParticleS();
495495
}
496496

497+
/** Communicates the provider name to the client (e.g., OpenRouter provider routing) */
498+
setProviderInfraLabel(label: string) {
499+
this.transmissionQueue.push({
500+
cg: 'set-provider-infra',
501+
label: label,
502+
});
503+
}
504+
497505
/** Communicates the upstream response handle, for remote control/resumability */
498506
setUpstreamHandle(handle: string, _type: 'oai-responses' /* the only one for now, used for type safety */) {
499507
if (SERVER_DEBUG_WIRE)

src/modules/aix/server/dispatch/chatGenerate/parsers/IParticleTransmitter.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ export interface IParticleTransmitter {
8585
/** Communicates the model name to the client */
8686
setModelName(modelName: string): void;
8787

88+
/** Communicates the provider name to the client (e.g., OpenRouter provider routing) */
89+
setProviderInfraLabel(label: string): void;
90+
8891
/** Communicates the upstream response handle, for remote control/resumability */
8992
setUpstreamHandle(handle: string, type: 'oai-responses'): void;
9093

src/modules/aix/server/dispatch/chatGenerate/parsers/openai.parser.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ export function createOpenAIChatCompletionsChunkParser(): ChatGenerateParseFunct
4141
let processedSearchResultUrls = new Set<string>();
4242
// NOTE: could compute rate (tok/s) from the first textful event to the last (to ignore the prefill time)
4343

44+
// [OpenRouter] Provider routing info - extracted from raw JSON before Zod strips it
45+
let openRouterProviderInfraSent = false;
46+
4447
// Supporting structure to accumulate the assistant message
4548
const accumulator: {
4649
content: string | null;
@@ -89,6 +92,12 @@ export function createOpenAIChatCompletionsChunkParser(): ChatGenerateParseFunct
8992
return;
9093
}
9194

95+
// [OpenRouter] Extract provider routing info (before Zod parsing strips unknown fields)
96+
if (!openRouterProviderInfraSent && typeof chunkData?.provider === 'string' && chunkData.provider) {
97+
openRouterProviderInfraSent = true;
98+
pt.setProviderInfraLabel(chunkData.provider);
99+
}
100+
92101
const json = OpenAIWire_API_Chat_Completions.ChunkResponse_schema.parse(chunkData);
93102

94103
// -> Model
@@ -409,6 +418,10 @@ export function createOpenAIChatCompletionsParserNS(): ChatGenerateParseFunction
409418
if (completeData.warning)
410419
console.log('AIX: OpenAI-dispatch-NS warning:', completeData.warning);
411420

421+
// [OpenRouter] Extract provider routing info (before Zod parsing strips unknown fields)
422+
if (typeof completeData?.provider === 'string' && completeData.provider)
423+
pt.setProviderInfraLabel(completeData.provider);
424+
412425
// Parse the complete response
413426

414427
// [Fixup, 2025-11-11] Some OpenAI-compatible APIs omit the 'object' field - inject it if needed

0 commit comments

Comments
 (0)