@x-all-in-one/coding-helper 0.4.6 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,6 +38,9 @@ export interface OpenCodeModelEntry {
38
38
  interleaved?: {
39
39
  field: string;
40
40
  };
41
+ provider?: {
42
+ npm: string;
43
+ };
41
44
  }
42
45
  export interface OpenCodeProviderConfig {
43
46
  npm: string;
@@ -62,6 +65,7 @@ export interface OpenCodeModelConfig {
62
65
  smallModel: string;
63
66
  baseUrl?: string;
64
67
  }
68
+ export declare function buildOpenCodeModelEntry(info: ModelInfo): OpenCodeModelEntry;
65
69
  /**
66
70
  * OpenCode 工具实现
67
71
  * 实现 ITool 接口,管理 OpenCode 的配置和生命周期
@@ -92,10 +96,6 @@ export declare class OpenCodeTool extends BaseTool {
92
96
  * Fetch available models from API
93
97
  */
94
98
  fetchAvailableModels(): Promise<string[]>;
95
- private hasTag;
96
- private isVisionModelByTag;
97
- private isReasoningModel;
98
- private isInterleavedReasoningModel;
99
99
  private buildModelsConfig;
100
100
  /**
101
101
  * Save model config to OpenCode (merge, not overwrite)
@@ -133,7 +133,7 @@ export declare class OpenCodeTool extends BaseTool {
133
133
  * Update oh-my-opencode agent and category models
134
134
  * Only updates the model field, preserves other configurations
135
135
  */
136
- updateOhMyOpenCodeModels(model: string): void;
136
+ updateOhMyOpenCodeModels(model: string, smallModel: string): void;
137
137
  /**
138
138
  * 获取所有已注册的插件
139
139
  */
@@ -10,6 +10,7 @@ const XAIO_PROVIDER_ID = 'xaio';
10
10
  // oh-my-opencode agent names (latest version)
11
11
  const OH_MY_OPENCODE_AGENTS = [
12
12
  'sisyphus',
13
+ 'hephaestus',
13
14
  'oracle',
14
15
  'librarian',
15
16
  'explore',
@@ -18,17 +19,30 @@ const OH_MY_OPENCODE_AGENTS = [
18
19
  'metis',
19
20
  'momus',
20
21
  'atlas',
22
+ 'frontend-ui-ux-engineer',
23
+ 'document-writer',
24
+ ];
25
+ const OH_MY_OPENCODE_SMALL_AGENTS = [
26
+ 'librarian',
27
+ 'explore',
28
+ 'document-writer',
21
29
  ];
22
30
  // oh-my-opencode category names
23
31
  const OH_MY_OPENCODE_CATEGORIES = [
24
32
  'visual-engineering',
25
33
  'ultrabrain',
34
+ 'deep',
26
35
  'artistry',
27
36
  'quick',
28
37
  'unspecified-low',
29
38
  'unspecified-high',
30
39
  'writing',
31
40
  ];
41
+ const OH_MY_OPENCODE_SMALL_CATEGORIES = [
42
+ 'quick',
43
+ 'unspecified-low',
44
+ 'writing',
45
+ ];
32
46
  // Unified output token limit for all models
33
47
  const OUTPUT_TOKEN_LIMIT = 32000;
34
48
  // Model tag constants
@@ -36,6 +50,18 @@ const TAG_VISION = '视觉';
36
50
  const TAG_REASONING = '推理';
37
51
  const TAG_INTERLEAVED_REASONING = '交替推理';
38
52
  const deepmerge = deepmergeCustom({ mergeArrays: false });
53
+ function hasTag(tags, tag) {
54
+ return Array.isArray(tags) && tags.includes(tag);
55
+ }
56
+ function isVisionModelByTag(tags) {
57
+ return hasTag(tags, TAG_VISION);
58
+ }
59
+ function isReasoningModel(tags) {
60
+ return hasTag(tags, TAG_REASONING) && !hasTag(tags, TAG_INTERLEAVED_REASONING);
61
+ }
62
+ function isInterleavedReasoningModel(tags) {
63
+ return hasTag(tags, TAG_INTERLEAVED_REASONING);
64
+ }
39
65
  // Default configuration
40
66
  export const OPENCODE_DEFAULT_CONFIG = {
41
67
  BASE_URL: 'https://code-api.x-aio.com/v1',
@@ -49,6 +75,45 @@ export const OPENCODE_DEFAULT_CONFIG = {
49
75
  VISION_CONTEXT: 64000,
50
76
  VISION_OUTPUT: OUTPUT_TOKEN_LIMIT,
51
77
  };
78
+ export function buildOpenCodeModelEntry(info) {
79
+ const entry = {};
80
+ const isVision = isVisionModelByTag(info.tags);
81
+ const isReasoning = isReasoningModel(info.tags);
82
+ const isInterleavedReasoning = isInterleavedReasoningModel(info.tags);
83
+ if (info.name && info.name !== info.id) {
84
+ entry.name = info.name;
85
+ }
86
+ if (isVision) {
87
+ entry.limit = {
88
+ context: info.context_length || OPENCODE_DEFAULT_CONFIG.VISION_CONTEXT,
89
+ output: OUTPUT_TOKEN_LIMIT,
90
+ };
91
+ entry.modalities = {
92
+ input: ['text', 'image', 'video'],
93
+ output: ['text'],
94
+ };
95
+ }
96
+ else {
97
+ entry.limit = {
98
+ context: info.context_length || OPENCODE_DEFAULT_CONFIG.DEFAULT_CONTEXT,
99
+ output: OUTPUT_TOKEN_LIMIT,
100
+ };
101
+ }
102
+ const isResponsesModel = info.upstream_type === 'responses';
103
+ if (isInterleavedReasoning) {
104
+ entry.reasoning = true;
105
+ if (!isResponsesModel) {
106
+ entry.interleaved = { field: 'reasoning_content' };
107
+ }
108
+ }
109
+ else if (isReasoning) {
110
+ entry.reasoning = true;
111
+ }
112
+ if (isResponsesModel) {
113
+ entry.provider = { npm: '@ai-sdk/openai' };
114
+ }
115
+ return entry;
116
+ }
52
117
  /**
53
118
  * OpenCode 工具实现
54
119
  * 实现 ITool 接口,管理 OpenCode 的配置和生命周期
@@ -119,52 +184,10 @@ export class OpenCodeTool extends BaseTool {
119
184
  async fetchAvailableModels() {
120
185
  return modelService.fetchModels();
121
186
  }
122
- hasTag(tags, tag) {
123
- return Array.isArray(tags) && tags.includes(tag);
124
- }
125
- isVisionModelByTag(tags) {
126
- return this.hasTag(tags, TAG_VISION);
127
- }
128
- isReasoningModel(tags) {
129
- return this.hasTag(tags, TAG_REASONING) && !this.hasTag(tags, TAG_INTERLEAVED_REASONING);
130
- }
131
- isInterleavedReasoningModel(tags) {
132
- return this.hasTag(tags, TAG_INTERLEAVED_REASONING);
133
- }
134
187
  buildModelsConfig(modelInfos) {
135
188
  const models = {};
136
189
  for (const info of modelInfos) {
137
- const entry = {};
138
- const isVision = this.isVisionModelByTag(info.tags);
139
- const isReasoning = this.isReasoningModel(info.tags);
140
- const isInterleavedReasoning = this.isInterleavedReasoningModel(info.tags);
141
- if (info.name && info.name !== info.id) {
142
- entry.name = info.name;
143
- }
144
- if (isVision) {
145
- entry.limit = {
146
- context: info.context_length || OPENCODE_DEFAULT_CONFIG.VISION_CONTEXT,
147
- output: OUTPUT_TOKEN_LIMIT,
148
- };
149
- entry.modalities = {
150
- input: ['text', 'image', 'video'],
151
- output: ['text'],
152
- };
153
- }
154
- else {
155
- entry.limit = {
156
- context: info.context_length || OPENCODE_DEFAULT_CONFIG.DEFAULT_CONTEXT,
157
- output: OUTPUT_TOKEN_LIMIT,
158
- };
159
- }
160
- if (isInterleavedReasoning) {
161
- entry.reasoning = true;
162
- entry.interleaved = { field: 'reasoning_content' };
163
- }
164
- else if (isReasoning) {
165
- entry.reasoning = true;
166
- }
167
- models[info.id] = entry;
190
+ models[info.id] = buildOpenCodeModelEntry(info);
168
191
  }
169
192
  return models;
170
193
  }
@@ -333,29 +356,35 @@ export class OpenCodeTool extends BaseTool {
333
356
  throw new Error(`Failed to save oh-my-opencode config: ${error}`);
334
357
  }
335
358
  }
336
- updateOhMyOpenCodeAgents(config, model) {
359
+ updateOhMyOpenCodeAgents(config, model, smallModel) {
337
360
  if (!config.agents) {
338
361
  config.agents = {};
339
362
  }
340
363
  for (const agentName of OH_MY_OPENCODE_AGENTS) {
364
+ const targetModel = OH_MY_OPENCODE_SMALL_AGENTS.includes(agentName)
365
+ ? smallModel
366
+ : model;
341
367
  if (config.agents[agentName]) {
342
- config.agents[agentName].model = model;
368
+ config.agents[agentName].model = targetModel;
343
369
  }
344
370
  else {
345
- config.agents[agentName] = { model };
371
+ config.agents[agentName] = { model: targetModel };
346
372
  }
347
373
  }
348
374
  }
349
- updateOhMyOpenCodeCategories(config, model) {
375
+ updateOhMyOpenCodeCategories(config, model, smallModel) {
350
376
  if (!config.categories) {
351
377
  config.categories = {};
352
378
  }
353
379
  for (const categoryName of OH_MY_OPENCODE_CATEGORIES) {
380
+ const targetModel = OH_MY_OPENCODE_SMALL_CATEGORIES.includes(categoryName)
381
+ ? smallModel
382
+ : model;
354
383
  if (config.categories[categoryName]) {
355
- config.categories[categoryName].model = model;
384
+ config.categories[categoryName].model = targetModel;
356
385
  }
357
386
  else {
358
- config.categories[categoryName] = { model };
387
+ config.categories[categoryName] = { model: targetModel };
359
388
  }
360
389
  }
361
390
  }
@@ -363,11 +392,12 @@ export class OpenCodeTool extends BaseTool {
363
392
  * Update oh-my-opencode agent and category models
364
393
  * Only updates the model field, preserves other configurations
365
394
  */
366
- updateOhMyOpenCodeModels(model) {
395
+ updateOhMyOpenCodeModels(model, smallModel) {
367
396
  const prefixedModel = `${XAIO_PROVIDER_ID}/${model}`;
397
+ const prefixedSmallModel = `${XAIO_PROVIDER_ID}/${smallModel}`;
368
398
  const currentConfig = this.getOhMyOpenCodeConfig();
369
- this.updateOhMyOpenCodeAgents(currentConfig, prefixedModel);
370
- this.updateOhMyOpenCodeCategories(currentConfig, prefixedModel);
399
+ this.updateOhMyOpenCodeAgents(currentConfig, prefixedModel, prefixedSmallModel);
400
+ this.updateOhMyOpenCodeCategories(currentConfig, prefixedModel, prefixedSmallModel);
371
401
  if (!currentConfig.$schema) {
372
402
  currentConfig.$schema = 'https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json';
373
403
  }
@@ -5,5 +5,6 @@ export interface ModelInfo {
5
5
  context_length?: number;
6
6
  max_output_tokens?: number;
7
7
  tags?: string[] | null;
8
+ upstream_type?: string;
8
9
  }
9
10
  export declare function fetchModelsFromApi(): Promise<ModelInfo[]>;
@@ -20,6 +20,7 @@ export async function fetchModelsFromApi() {
20
20
  context_length: m.context ? m.context * 1000 : undefined,
21
21
  max_output_tokens: m.context ? m.context * 500 : undefined,
22
22
  tags: Array.isArray(m.tags) ? m.tags : null,
23
+ upstream_type: typeof m.upstream_type === 'string' ? m.upstream_type : undefined,
23
24
  }));
24
25
  }
25
26
  return [];
@@ -117,8 +117,8 @@ export class PluginMenu {
117
117
  const result = await modelService.refreshAndSyncToOpenCode();
118
118
  if (result.success) {
119
119
  const modelConfig = openCodeTool.getModelConfig();
120
- if (modelConfig?.model) {
121
- openCodeTool.updateOhMyOpenCodeModels(modelConfig.model);
120
+ if (modelConfig?.model && modelConfig.smallModel) {
121
+ openCodeTool.updateOhMyOpenCodeModels(modelConfig.model, modelConfig.smallModel);
122
122
  }
123
123
  spinner.succeed(chalk.green(i18n.t('wizard.models_refreshed', { count: String(result.count) })));
124
124
  }
@@ -364,7 +364,7 @@ export class ToolMenu {
364
364
  // Update oh-my-opencode agent models if plugin is installed
365
365
  if (toolName === 'opencode' && openCodeTool.hasOhMyOpenCodePlugin()) {
366
366
  spinner.text = i18n.t('wizard.oh_my_opencode_detected');
367
- openCodeTool.updateOhMyOpenCodeModels(openCodeModel);
367
+ openCodeTool.updateOhMyOpenCodeModels(openCodeModel, openCodeSmallModel);
368
368
  await new Promise(resolve => setTimeout(resolve, 500));
369
369
  console.log(chalk.gray(` ${i18n.t('wizard.oh_my_opencode_models_updated')}`));
370
370
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@x-all-in-one/coding-helper",
3
3
  "type": "module",
4
- "version": "0.4.6",
4
+ "version": "0.4.7",
5
5
  "description": "X All In One Coding Helper",
6
6
  "author": "X.AIO",
7
7
  "homepage": "https://docs.x-aio.com/zh/docs",
@@ -69,6 +69,7 @@
69
69
  "lint": "eslint .",
70
70
  "lint:fix": "eslint . --fix",
71
71
  "type-check": "tsc --noEmit",
72
+ "verify:opencode-provider": "tsx scripts/verify-opencode-responses-provider.ts",
72
73
  "changeset": "changeset",
73
74
  "version": "changeset version",
74
75
  "release": "pnpm run build && changeset publish"