Skip to content

Commit 688f479

Browse files
Revert "feat: move model related data to meta"
This reverts commit b5f470c.
1 parent 6a6c3b4 commit 688f479

19 files changed

Lines changed: 128 additions & 307 deletions

src/CodexAcpClient.ts

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ import type {Disposable} from "vscode-jsonrpc";
1313
import type {
1414
ClientInfo,
1515
ReasoningEffort,
16-
ServerNotification,
17-
ServiceTier,
16+
ServerNotification
1817
} from "./app-server";
1918
import type {JsonValue} from "./app-server/serde_json/JsonValue";
19+
import {ModelId} from "./ModelId";
2020
import {AgentMode} from "./AgentMode";
2121
import path from "node:path";
2222
import {logger} from "./Logger";
@@ -215,10 +215,10 @@ export class CodexAcpClient {
215215
threadId: request.sessionId,
216216
});
217217
const codexModels = await this.fetchAvailableModels();
218-
const modelSelection = this.createModelSelection(codexModels, response.model, response.reasoningEffort, response.serviceTier);
218+
const currentModelId = this.createModelId(codexModels, response.model, response.reasoningEffort).toString();
219219
return {
220220
sessionId: request.sessionId,
221-
...modelSelection,
221+
currentModelId: currentModelId,
222222
models: codexModels,
223223
}
224224
}
@@ -237,10 +237,10 @@ export class CodexAcpClient {
237237
threadId: request.sessionId,
238238
});
239239
const codexModels = await this.fetchAvailableModels();
240-
const modelSelection = this.createModelSelection(codexModels, response.model, response.reasoningEffort, response.serviceTier);
240+
const currentModelId = this.createModelId(codexModels, response.model, response.reasoningEffort).toString();
241241
return {
242242
sessionId: request.sessionId,
243-
...modelSelection,
243+
currentModelId: currentModelId,
244244
models: codexModels,
245245
thread: response.thread,
246246
};
@@ -266,10 +266,10 @@ export class CodexAcpClient {
266266
if (codexModels.length === 0) {
267267
throw new Error("Codex did not return any models");
268268
}
269-
const modelSelection = this.createModelSelection(codexModels, response.model, response.reasoningEffort, response.serviceTier);
269+
const currentModelId = this.createModelId(codexModels, response.model, response.reasoningEffort).toString();
270270
return {
271271
sessionId: response.thread.id,
272-
...modelSelection,
272+
currentModelId: currentModelId,
273273
models: codexModels,
274274
};
275275
}
@@ -364,15 +364,10 @@ export class CodexAcpClient {
364364
}
365365

366366
/**
367-
* Resolves a model selection using the provided ID and reasoning effort.
368-
* Falls back to model defaults if parameters are missing.
367+
* Resolves a ModelId using the provided ID and reasoning effort.
368+
* Falls back to model defaults if parameters are missing or unsupported.
369369
*/
370-
createModelSelection(
371-
availableModels: Model[],
372-
modelId: string | null,
373-
reasoningEffort: ReasoningEffort | null,
374-
serviceTier: ServiceTier | null,
375-
): ModelSelection {
370+
createModelId(availableModels: Model[], modelId: string | null, reasoningEffort: ReasoningEffort | null): ModelId {
376371
const selectedModel =
377372
availableModels.find(m => m.id === modelId) ??
378373
availableModels.find(m => m.isDefault);
@@ -381,11 +376,7 @@ export class CodexAcpClient {
381376
throw new Error(`Model selection failed: No model found for ID "${modelId}" and no default model is defined.`);
382377
}
383378

384-
return {
385-
currentModelId: selectedModel.id,
386-
currentReasoningEffort: reasoningEffort ?? selectedModel.defaultReasoningEffort,
387-
currentServiceTier: serviceTier ?? null,
388-
};
379+
return ModelId.create(selectedModel.id, reasoningEffort ?? selectedModel.defaultReasoningEffort);
389380
}
390381

391382
async subscribeToSessionEvents(
@@ -402,11 +393,12 @@ export class CodexAcpClient {
402393
async sendPrompt(
403394
request: acp.PromptRequest,
404395
agentMode: AgentMode,
405-
modelSelection: ModelSelection,
396+
modelId: ModelId,
406397
disableSummary: boolean,
407398
cwd: string,
408399
): Promise<TurnCompletedNotification> {
409400
const input = buildPromptItems(request.prompt);
401+
const effort = modelId.effort as ReasoningEffort | null; //TODO remove unsafe conversion
410402

411403
await this.refreshSkills(cwd, request._meta);
412404
return await this.codexClient.runTurn({
@@ -418,9 +410,9 @@ export class CodexAcpClient {
418410
summary: disableSummary ? "none" : null,
419411
personality: null,
420412
cwd: null,
421-
effort: modelSelection.currentReasoningEffort,
422-
model: modelSelection.currentModelId,
423-
serviceTier: modelSelection.currentServiceTier,
413+
effort: effort,
414+
model: modelId.model,
415+
serviceTier: modelId.serviceTier,
424416
});
425417
}
426418

@@ -613,17 +605,13 @@ export type JsonObject = { [key in string]?: JsonValue }
613605
export type SessionMetadata = {
614606
sessionId: string,
615607
currentModelId: string,
616-
currentReasoningEffort: ReasoningEffort,
617-
currentServiceTier: ServiceTier | null,
618608
models: Model[],
619609
}
620610

621611
export type SessionMetadataWithThread = SessionMetadata & {
622612
thread: Thread,
623613
}
624614

625-
export type ModelSelection = Pick<SessionMetadata, "currentModelId" | "currentReasoningEffort" | "currentServiceTier">;
626-
627615
function buildPromptItems(prompt: acp.ContentBlock[]): UserInput[] {
628616
return prompt.map((block): UserInput | null => {
629617
switch (block.type) {

src/CodexAcpServer.ts

Lines changed: 50 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import {CodexEventHandler} from "./CodexEventHandler";
44
import {CodexApprovalHandler} from "./CodexApprovalHandler";
55
import {CodexElicitationHandler} from "./CodexElicitationHandler";
66
import {type CodexAuthRequest, getCodexAuthMethods} from "./CodexAuthMethod";
7-
import {CodexAcpClient, type ModelSelection, type SessionMetadata, type SessionMetadataWithThread} from "./CodexAcpClient";
7+
import {CodexAcpClient, type SessionMetadata, type SessionMetadataWithThread} from "./CodexAcpClient";
88
import type {McpStartupResult} from "./CodexAppServerClient";
99
import {ACPSessionConnection, type UpdateSessionEvent} from "./ACPSessionConnection";
10-
import type {InputModality, ReasoningEffort, ServiceTier} from "./app-server";
10+
import type {InputModality, ReasoningEffort} from "./app-server";
1111
import type {
1212
Account,
1313
CollabAgentToolCallStatus,
@@ -18,6 +18,7 @@ import type {
1818
UserInput
1919
} from "./app-server/v2";
2020
import type {RateLimitsMap} from "./RateLimitsMap";
21+
import {ModelId} from "./ModelId";
2122
import {AgentMode} from "./AgentMode";
2223
import type {TokenCount} from "./TokenCount";
2324
import {toPromptUsage} from "./TokenCount";
@@ -35,8 +36,6 @@ import {
3536
export interface SessionState {
3637
sessionId: string,
3738
currentModelId: string,
38-
currentReasoningEffort: ReasoningEffort,
39-
currentServiceTier: ServiceTier | null,
4039
supportedReasoningEfforts: Array<ReasoningEffortOption>,
4140
supportedInputModalities: Array<InputModality>,
4241
agentMode: AgentMode,
@@ -161,14 +160,12 @@ export class CodexAcpServer implements acp.Agent {
161160
}
162161

163162
const account = await this.getActiveAccount();
164-
const {sessionId, currentModelId, currentReasoningEffort, currentServiceTier, models} = sessionMetadata;
163+
const {sessionId, currentModelId, models} = sessionMetadata;
165164
const sessionMcpServers = this.resolveSessionMcpServers(requestedMcpServers, "sessionId" in request);
166165
const currentModel = this.findCurrentModel(models, currentModelId);
167166
const sessionState: SessionState = {
168167
sessionId: sessionId,
169168
currentModelId: currentModelId,
170-
currentReasoningEffort: currentReasoningEffort,
171-
currentServiceTier: currentServiceTier,
172169
supportedReasoningEfforts: currentModel?.supportedReasoningEfforts ?? [],
173170
supportedInputModalities: currentModel?.inputModalities ?? ["text", "image"],
174171
agentMode: AgentMode.getInitialAgentMode(),
@@ -192,7 +189,7 @@ export class CodexAcpServer implements acp.Agent {
192189
}
193190

194191
this.publishAvailableCommandsAsync(sessionId);
195-
const sessionModelState: SessionModelState = this.createModelState(models, sessionState);
192+
const sessionModelState: SessionModelState = this.createModelState(models, currentModelId);
196193
const sessionModeState: SessionModeState = sessionState.agentMode.toSessionModeState();
197194

198195
return [sessionId, sessionModelState, sessionModeState];
@@ -313,32 +310,34 @@ export class CodexAcpServer implements acp.Agent {
313310
const sessionState = this.sessions.get(params.sessionId);
314311
if (!sessionState) throw new Error(`Session ${params.sessionId} not found`);
315312

313+
const requestedModelId = ModelId.fromString(params.modelId);
314+
const requestedModelName = requestedModelId.model;
315+
const requestedEffort = requestedModelId.effort;
316+
316317
const models = await this.codexAcpClient.fetchAvailableModels();
317-
const model = models.find(m => m.id === params.modelId);
318+
const model = models.find(m => m.id === requestedModelName);
318319
if (!model) throw new Error(`Unknown model ${params.modelId}`);
320+
if (requestedModelId.serviceTier === "fast" && !model.additionalSpeedTiers.includes("fast")) {
321+
throw new Error(`Unsupported service tier fast for model ${requestedModelName}`);
322+
}
319323

320-
const requestedEffort = readStringMeta(params._meta, "reasoningEffort") as ReasoningEffort | null;
321-
let reasoningEffort = model.defaultReasoningEffort;
322-
if (requestedEffort !== null) {
324+
const requestedEffortValue = requestedEffort as ReasoningEffort | undefined;
325+
let reasoningEffort: ReasoningEffort;
326+
if (requestedEffortValue) {
323327
const matchedEffort = model.supportedReasoningEfforts.find(
324-
(option) => option.reasoningEffort === requestedEffort
328+
(option) => option.reasoningEffort === requestedEffortValue
325329
)?.reasoningEffort;
326330

327331
if (!matchedEffort) {
328-
throw new Error(`Unsupported reasoning effort ${requestedEffort} for model ${model.id}`);
332+
throw new Error(`Unsupported reasoning effort ${requestedEffortValue} for model ${requestedModelName}`);
329333
}
330334

331335
reasoningEffort = matchedEffort;
336+
} else {
337+
reasoningEffort = model.defaultReasoningEffort;
332338
}
333339

334-
const requestedServiceTier = readStringMeta(params._meta, "serviceTier") as ServiceTier | null;
335-
if (requestedServiceTier !== null && !model.additionalSpeedTiers.includes(requestedServiceTier)) {
336-
throw new Error(`Unsupported service tier ${requestedServiceTier} for model ${model.id}`);
337-
}
338-
339-
sessionState.currentModelId = model.id;
340-
sessionState.currentReasoningEffort = reasoningEffort;
341-
sessionState.currentServiceTier = requestedServiceTier;
340+
sessionState.currentModelId = ModelId.fromComponents(model, reasoningEffort, requestedModelId.serviceTier).toString();
342341
sessionState.supportedReasoningEfforts = model.supportedReasoningEfforts;
343342
sessionState.supportedInputModalities = model.inputModalities;
344343

@@ -350,28 +349,35 @@ export class CodexAcpServer implements acp.Agent {
350349
}
351350

352351
private findCurrentModel(models: Model[], currentModelId: string): Model | undefined {
353-
return models.find(m => m.id === currentModelId);
352+
const modelId = ModelId.fromString(currentModelId);
353+
return models.find(m => m.id === modelId.model);
354354
}
355355

356-
private createModelState(availableModels: Model[], selection: ModelSelection): SessionModelState {
356+
private createModelState(availableModels: Model[], selectedModelId: string): SessionModelState {
357357
const allowedModels = availableModels
358-
.map((model) => ({
359-
modelId: model.id,
360-
name: model.displayName,
361-
description: model.description,
362-
_meta: {
363-
supportedReasoningEfforts: model.supportedReasoningEfforts,
364-
defaultReasoningEffort: model.defaultReasoningEffort,
365-
serviceTiers: model.additionalSpeedTiers,
366-
},
367-
}));
358+
.flatMap((model) =>
359+
model.supportedReasoningEfforts.flatMap((effort) => {
360+
const standardModel = {
361+
modelId: ModelId.fromComponents(model, effort.reasoningEffort).toString(),
362+
name: `${model.displayName} (${effort.reasoningEffort})`,
363+
description: `${model.description} ${effort.description}`,
364+
};
365+
if (!model.additionalSpeedTiers.includes("fast")) {
366+
return [standardModel];
367+
}
368+
return [
369+
standardModel,
370+
{
371+
modelId: ModelId.fromComponents(model, effort.reasoningEffort, "fast").toString(),
372+
name: `${model.displayName} (${effort.reasoningEffort}, fast)`,
373+
description: `${model.description} ${effort.description} Fast service tier.`,
374+
},
375+
];
376+
})
377+
);
368378
return {
369379
availableModels: allowedModels,
370-
currentModelId: selection.currentModelId,
371-
_meta: {
372-
currentReasoningEffort: selection.currentReasoningEffort,
373-
currentServiceTier: selection.currentServiceTier,
374-
},
380+
currentModelId: selectedModelId,
375381
}
376382
}
377383

@@ -395,14 +401,12 @@ export class CodexAcpServer implements acp.Agent {
395401
);
396402

397403
const account = await this.getActiveAccount();
398-
const {sessionId, currentModelId, currentReasoningEffort, currentServiceTier, models, thread} = sessionMetadata;
404+
const {sessionId, currentModelId, models, thread} = sessionMetadata;
399405
const sessionMcpServers = this.resolveSessionMcpServers(requestedMcpServers, true);
400406
const currentModel = this.findCurrentModel(models, currentModelId);
401407
const sessionState: SessionState = {
402408
sessionId: sessionId,
403409
currentModelId: currentModelId,
404-
currentReasoningEffort: currentReasoningEffort,
405-
currentServiceTier: currentServiceTier,
406410
supportedReasoningEfforts: currentModel?.supportedReasoningEfforts ?? [],
407411
supportedInputModalities: currentModel?.inputModalities ?? ["text", "image"],
408412
agentMode: AgentMode.getInitialAgentMode(),
@@ -426,7 +430,7 @@ export class CodexAcpServer implements acp.Agent {
426430
}
427431

428432
await this.availableCommands.publish(sessionId);
429-
const sessionModelState: SessionModelState = this.createModelState(models, sessionState);
433+
const sessionModelState: SessionModelState = this.createModelState(models, currentModelId);
430434
const sessionModeState: SessionModeState = sessionState.agentMode.toSessionModeState();
431435

432436
return {
@@ -763,6 +767,7 @@ export class CodexAcpServer implements acp.Agent {
763767
};
764768
}
765769

770+
const modelId = ModelId.fromString(sessionState.currentModelId);
766771
const modelLacksReasoning = sessionState.supportedReasoningEfforts.length > 0
767772
&& sessionState.supportedReasoningEfforts.every(e => e.reasoningEffort === "none");
768773

@@ -779,7 +784,7 @@ export class CodexAcpServer implements acp.Agent {
779784
}
780785
const agentMode = sessionState.agentMode;
781786
const turnCompleted = await this.runWithProcessCheck(
782-
() => this.codexAcpClient.sendPrompt(params, agentMode, sessionState, disableSummary, sessionState.cwd));
787+
() => this.codexAcpClient.sendPrompt(params, agentMode, modelId, disableSummary, sessionState.cwd));
783788

784789
// Check if turn was interrupted (cancelled)
785790
if (turnCompleted.turn.status === "interrupted") {
@@ -823,7 +828,7 @@ export class CodexAcpServer implements acp.Agent {
823828
private buildQuotaMeta(sessionState: SessionState): { quota: QuotaMeta } {
824829
const lastTokenUsage = sessionState.lastTokenUsage;
825830

826-
const modelName = sessionState.currentModelId;
831+
const modelName = ModelId.fromString(sessionState.currentModelId).model;
827832

828833
// FIXME: currently all tokens are reported for the current model
829834
const modelUsage = (lastTokenUsage != null)
@@ -896,14 +901,3 @@ export class CodexAcpServer implements acp.Agent {
896901
function getRequestedMcpServerNames(mcpServers: Array<acp.McpServer>): Array<string> {
897902
return Array.from(new Set(mcpServers.map(server => server.name)));
898903
}
899-
900-
function readStringMeta(meta: { [key: string]: unknown } | null | undefined, key: string): string | null {
901-
const value = meta?.[key];
902-
if (value === undefined || value === null) {
903-
return null;
904-
}
905-
if (typeof value !== "string") {
906-
throw RequestError.invalidParams(`Expected _meta.${key} to be a string`);
907-
}
908-
return value;
909-
}

0 commit comments

Comments
 (0)