Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions src/agents/plugins/__tests__/codemie-code-reasoning.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ describe('CodeMie Code Plugin — Reasoning Sanitization Integration', () => {
const beforeRun = CodeMieCodePluginMetadata.lifecycle!.beforeRun!;
const onSessionEnd = CodeMieCodePluginMetadata.lifecycle!.onSessionEnd!;

it('declares the complete reasoning-effort range for OpenCode forwarding', () => {
expect(CodeMieCodePluginMetadata.reasoningEffort).toMatchObject({
strategy: 'cli-flag',
flag: '--variant',
supportedLevels: ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'],
});
});

beforeEach(() => {
vi.clearAllMocks();
mockDiscoverSessions.mockResolvedValue([]);
Expand Down
29 changes: 29 additions & 0 deletions src/agents/plugins/__tests__/opencode-gpt55-routing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,28 @@ describe('GPT-5.6 → Responses API routing', () => {
expect(config.limit.context).toBe(1050000);
});

it('dynamic GPT-5.6 config preserves the complete effort-level map', () => {
const config = convertApiModelToOpenCodeConfig(makeLlmModel('openai.gpt-5.6-luna'));
expect(Object.keys(config.variants ?? {})).toEqual(['minimal', 'none', 'low', 'medium', 'high', 'xhigh', 'max']);
expect(config.variants?.minimal?.reasoningEffort).toBe('none');
expect(config.variants?.none?.reasoningEffort).toBe('none');
expect(config.variants?.xhigh?.reasoningEffort).toBe('xhigh');
expect(config.variants?.max?.reasoningEffort).toBe('max');
});

it.each([
['gpt-5.6-luna', 'none'],
['gpt-5.6-terra', 'none'],
['gpt-6-sol', 'none'],
['gpt-6-luna', 'none'],
['gpt-6-astra', 'low'],
['gpt-6.1-sol', 'low'],
])('%s maps CodeMie minimal to native %s', (modelId, nativeMinimal) => {
const config = convertApiModelToOpenCodeConfig(makeLlmModel(modelId));
expect(config.use_responses_api).toBe(true);
expect(config.variants?.minimal?.reasoningEffort).toBe(nativeMinimal);
});

// ── Static fallback path (OPENCODE_MODEL_CONFIGS) ──────────────────────────

it('static config has gpt-5.6-sol-2026-07-09 with use_responses_api: true', () => {
Expand All @@ -164,6 +186,13 @@ describe('GPT-5.6 → Responses API routing', () => {
it('static config gpt-5.6-sol-2026-07-09 reports context limit of 1050000', () => {
expect(OPENCODE_MODEL_CONFIGS['gpt-5.6-sol-2026-07-09']!.limit.context).toBe(1050000);
});

it('static GPT-5.6 config preserves xhigh and max', () => {
const variants = OPENCODE_MODEL_CONFIGS['gpt-5.6-sol-2026-07-09']!.variants;
expect(variants?.minimal?.reasoningEffort).toBe('none');
expect(variants?.xhigh?.reasoningEffort).toBe('xhigh');
expect(variants?.max?.reasoningEffort).toBe('max');
});
});

describe('GPT-6 → Responses API routing', () => {
Expand Down
8 changes: 8 additions & 0 deletions src/agents/plugins/codemie-code.plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,14 @@ export const CodeMieCodePluginMetadata: AgentMetadata = {

supportedProviders: ['litellm', 'ai-run-sso', 'ollama', 'bedrock', 'bearer-auth'],

reasoningEffort: {
strategy: 'cli-flag',
flag: '--variant',
placement: 'append',
supportedLevels: ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'],
userOverrideFlags: ['--variant'],
},

ssoConfig: { enabled: true, clientType: 'codemie-code' },

lifecycle: {
Expand Down
4 changes: 3 additions & 1 deletion src/agents/plugins/opencode/opencode-dynamic-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import type { LlmModel } from '../../../providers/plugins/sso/sso.http-client.js';
import { fetchCodeMieLlmModels, isRouterModel } from '../../../providers/plugins/sso/sso.http-client.js';
import type { OpenCodeModelConfig } from './opencode-model-configs.js';
import { OPENCODE_MODEL_CONFIGS } from './opencode-model-configs.js';
import { getExtendedReasoningVariants, OPENCODE_MODEL_CONFIGS } from './opencode-model-configs.js';
import { CodeMieSSO } from '../../../providers/plugins/sso/sso.auth.js';
import { logger } from '../../../utils/logger.js';

Expand Down Expand Up @@ -109,6 +109,7 @@ export function convertApiModelToOpenCodeConfig(model: LlmModel, isSelected = fa
const family = detectFamily(id);
const limit = detectLimits(id, family);
const responsesApi = isResponsesApiModel(id);
const extendedVariants = responsesApi ? getExtendedReasoningVariants(id) : undefined;

const toPerMillion = (v: number | undefined) => (v ?? 0) * 1_000_000;

Expand All @@ -131,6 +132,7 @@ export function convertApiModelToOpenCodeConfig(model: LlmModel, isSelected = fa
temperature: model.features?.temperature ?? true,
structured_output: model.features?.tools ? true : undefined,
...(responsesApi && { use_responses_api: true }),
...(extendedVariants && { variants: extendedVariants }),
modalities: {
input: model.multimodal ? ['text', 'image'] : ['text'],
output: ['text'],
Expand Down
67 changes: 67 additions & 0 deletions src/agents/plugins/opencode/opencode-model-configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,70 @@ export interface OpenCodeModelConfig {
headers?: Record<string, string>;
timeout?: number;
};
/** Named model variants used by OpenCode's --variant flag. */
variants?: Record<string, Record<string, unknown>>;
}

const GPT_5_6_REASONING_LEVELS = ['none', 'low', 'medium', 'high', 'xhigh', 'max'] as const;
const GPT_6_REASONING_LEVELS_FROM_LOW = ['low', 'medium', 'high', 'xhigh', 'max'] as const;

function getNativeReasoningLevels(modelId: string): readonly string[] | undefined {
if (/gpt-5[.-]6(?:[.-]|$)/.test(modelId)) {
return GPT_5_6_REASONING_LEVELS;
}

// GPT-6 Astra and GPT-6.1 Sol do not expose either `none` or `minimal`.
if (
/gpt-6[.-]astra(?:[.-]|$)/.test(modelId) ||
/gpt-6[.-]1(?:[.-]|$)/.test(modelId)
) {
return GPT_6_REASONING_LEVELS_FROM_LOW;
}

// GPT-6 Sol and Luna expose `none`, but not `minimal`.
if (/gpt-6(?:[.-]|$)/.test(modelId)) {
return GPT_5_6_REASONING_LEVELS;
}

return undefined;
}

/**
* Return explicit OpenAI Responses variants for models with model-specific
* reasoning vocabularies. CodeMie's `minimal` is an alias for the model's
* lowest native setting when OpenAI does not expose a native `minimal` value.
* OpenCode's provider defaults do not define `max`, so it must be present in
* the injected model configuration.
*/
export function getExtendedReasoningVariants(
modelId: string,
): Record<string, Record<string, unknown>> | undefined {
const nativeLevels = getNativeReasoningLevels(modelId);
if (!nativeLevels) return undefined;

const minimalTarget = nativeLevels.includes('none') ? 'none' : 'low';
const nativeVariants = nativeLevels.map(level => [
level,
{
reasoningEffort: level,
reasoningSummary: 'auto',
include: ['reasoning.encrypted_content'],
},
] as const);

return Object.fromEntries(
[
['minimal', {
reasoningEffort: minimalTarget,
reasoningSummary: 'auto',
include: ['reasoning.encrypted_content'],
}],
...nativeVariants,
].map(([level, options]) => [
level,
options,
]),
);
}

export const OPENCODE_MODEL_CONFIGS: Record<string, OpenCodeModelConfig> = {
Expand Down Expand Up @@ -269,6 +333,7 @@ export const OPENCODE_MODEL_CONFIGS: Record<string, OpenCodeModelConfig> = {
temperature: false,
structured_output: true,
use_responses_api: true,
variants: getExtendedReasoningVariants('gpt-5.6-sol-2026-07-09'),
modalities: {
input: ['text', 'image'],
output: ['text']
Expand Down Expand Up @@ -816,6 +881,7 @@ export function getModelConfig(modelId: string): OpenCodeModelConfig {
prefix => modelId.startsWith(prefix)
);
const familyDefaults = familyPrefix ? MODEL_FAMILY_DEFAULTS[familyPrefix] : {};
const extendedVariants = getExtendedReasoningVariants(modelId);

// Extract family from model ID (e.g., "gpt-4o" -> "gpt-4", "claude-4-5-sonnet" -> "claude-4")
const family = familyDefaults.family
Expand All @@ -839,6 +905,7 @@ export function getModelConfig(modelId: string): OpenCodeModelConfig {
release_date: today,
last_updated: today,
open_weights: false,
...(extendedVariants && { use_responses_api: true, variants: extendedVariants }),
cost: { input: 0, output: 0 },
limit: familyDefaults.limit ?? { context: 128000, output: 4096 }
};
Expand Down
Loading