@x-all-in-one/coding-helper 0.4.2 → 0.4.4

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.
@@ -30,6 +30,10 @@ export interface OpenCodeModelEntry {
30
30
  input?: string[];
31
31
  output?: string[];
32
32
  };
33
+ reasoning?: boolean;
34
+ interleaved?: {
35
+ field: string;
36
+ };
33
37
  }
34
38
  export interface OpenCodeProviderConfig {
35
39
  npm: string;
@@ -84,13 +88,10 @@ export declare class OpenCodeTool extends BaseTool {
84
88
  * Fetch available models from API
85
89
  */
86
90
  fetchAvailableModels(): Promise<string[]>;
87
- /**
88
- * Check if model is a vision model (contains "VL" or ends with "V")
89
- */
90
- private isVisionModel;
91
- /**
92
- * Build models object from fetched model list
93
- */
91
+ private hasTag;
92
+ private isVisionModelByTag;
93
+ private isReasoningModel;
94
+ private isInterleavedReasoningModel;
94
95
  private buildModelsConfig;
95
96
  /**
96
97
  * Save model config to OpenCode (merge, not overwrite)
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import { dirname, join } from 'node:path';
4
+ import { deepmergeCustom } from 'deepmerge-ts';
4
5
  import { configManager } from '../config.js';
5
6
  import { modelService } from '../model-service.js';
6
7
  import { BaseTool } from './base-tool.js';
@@ -18,6 +19,11 @@ const OH_MY_OPENCODE_AGENTS = [
18
19
  ];
19
20
  // Unified output token limit for all models
20
21
  const OUTPUT_TOKEN_LIMIT = 32000;
22
+ // Model tag constants
23
+ const TAG_VISION = '视觉';
24
+ const TAG_REASONING = '推理';
25
+ const TAG_INTERLEAVED_REASONING = '交替推理';
26
+ const deepmerge = deepmergeCustom({ mergeArrays: false });
21
27
  // Default configuration
22
28
  export const OPENCODE_DEFAULT_CONFIG = {
23
29
  BASE_URL: 'https://code-api.x-aio.com/v1',
@@ -101,32 +107,33 @@ export class OpenCodeTool extends BaseTool {
101
107
  async fetchAvailableModels() {
102
108
  return modelService.fetchModels();
103
109
  }
104
- /**
105
- * Check if model is a vision model (contains "VL" or ends with "V")
106
- */
107
- isVisionModel(modelId) {
108
- const upperCaseId = modelId.toUpperCase();
109
- return upperCaseId.includes('VL') || upperCaseId.endsWith('V');
110
+ hasTag(tags, tag) {
111
+ return Array.isArray(tags) && tags.includes(tag);
112
+ }
113
+ isVisionModelByTag(tags) {
114
+ return this.hasTag(tags, TAG_VISION);
115
+ }
116
+ isReasoningModel(tags) {
117
+ return this.hasTag(tags, TAG_REASONING) && !this.hasTag(tags, TAG_INTERLEAVED_REASONING);
118
+ }
119
+ isInterleavedReasoningModel(tags) {
120
+ return this.hasTag(tags, TAG_INTERLEAVED_REASONING);
110
121
  }
111
- /**
112
- * Build models object from fetched model list
113
- */
114
122
  buildModelsConfig(modelInfos) {
115
123
  const models = {};
116
124
  for (const info of modelInfos) {
117
125
  const entry = {};
118
- const isVision = this.isVisionModel(info.id);
119
- // Set display name
126
+ const isVision = this.isVisionModelByTag(info.tags);
127
+ const isReasoning = this.isReasoningModel(info.tags);
128
+ const isInterleavedReasoning = this.isInterleavedReasoningModel(info.tags);
120
129
  if (info.name && info.name !== info.id) {
121
130
  entry.name = info.name;
122
131
  }
123
- // Set limits (context from API, output always use unified limit)
124
132
  if (isVision) {
125
133
  entry.limit = {
126
134
  context: info.context_length || OPENCODE_DEFAULT_CONFIG.VISION_CONTEXT,
127
135
  output: OUTPUT_TOKEN_LIMIT,
128
136
  };
129
- // Add modalities for vision models
130
137
  entry.modalities = {
131
138
  input: ['text', 'image', 'video'],
132
139
  output: ['text'],
@@ -138,6 +145,13 @@ export class OpenCodeTool extends BaseTool {
138
145
  output: OUTPUT_TOKEN_LIMIT,
139
146
  };
140
147
  }
148
+ if (isInterleavedReasoning) {
149
+ entry.reasoning = true;
150
+ entry.interleaved = { field: 'reasoning_content' };
151
+ }
152
+ else if (isReasoning) {
153
+ entry.reasoning = true;
154
+ }
141
155
  models[info.id] = entry;
142
156
  }
143
157
  return models;
@@ -160,7 +174,8 @@ export class OpenCodeTool extends BaseTool {
160
174
  }
161
175
  // Read current config (preserve user's other settings)
162
176
  const currentConfig = this.getConfig();
163
- // Build xaio provider config
177
+ const existingModels = currentConfig.provider?.[XAIO_PROVIDER_ID]?.models || {};
178
+ const mergedModels = deepmerge(existingModels, modelsConfig);
164
179
  const xaioProvider = {
165
180
  npm: '@ai-sdk/openai-compatible',
166
181
  name: OPENCODE_DEFAULT_CONFIG.PROVIDER_NAME,
@@ -168,7 +183,7 @@ export class OpenCodeTool extends BaseTool {
168
183
  baseURL: `https://${configManager.baseUrl}/v1`,
169
184
  apiKey: modelConfig.apiKey,
170
185
  },
171
- models: modelsConfig,
186
+ models: mergedModels,
172
187
  };
173
188
  // Merge config: only update xaio provider, preserve other providers
174
189
  const newConfig = {
@@ -259,9 +274,8 @@ export class OpenCodeTool extends BaseTool {
259
274
  }
260
275
  currentConfig.provider[XAIO_PROVIDER_ID] = xaioProvider;
261
276
  }
262
- // 更新 models 部分
263
277
  const modelsConfig = this.buildModelsConfig(modelInfos);
264
- xaioProvider.models = modelsConfig;
278
+ xaioProvider.models = deepmerge(xaioProvider.models || {}, modelsConfig);
265
279
  this.saveConfig(currentConfig);
266
280
  }
267
281
  // ==================== Oh My OpenCode 相关方法 ====================
@@ -4,5 +4,6 @@ export interface ModelInfo {
4
4
  name?: string;
5
5
  context_length?: number;
6
6
  max_output_tokens?: number;
7
+ tags?: string[] | null;
7
8
  }
8
9
  export declare function fetchModelsFromApi(): Promise<ModelInfo[]>;
@@ -19,6 +19,7 @@ export async function fetchModelsFromApi() {
19
19
  name: m.real_model_name,
20
20
  context_length: m.context ? m.context * 1000 : undefined,
21
21
  max_output_tokens: m.context ? m.context * 500 : undefined,
22
+ tags: Array.isArray(m.tags) ? m.tags : null,
22
23
  }));
23
24
  }
24
25
  return [];
@@ -1,5 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import inquirer from 'inquirer';
3
+ import { configManager } from '../../config.js';
3
4
  import { i18n } from '../../i18n.js';
4
5
  import { promptHelper } from '../ui/prompt-helper.js';
5
6
  import { uiRenderer } from '../ui/ui-renderer.js';
@@ -51,7 +52,8 @@ export class LanguageFlow {
51
52
  if (language === 'back') {
52
53
  return 'back';
53
54
  }
54
- await i18n.setLocale(language);
55
+ configManager.setLang(language);
56
+ i18n.loadFromConfig(language);
55
57
  console.log(chalk.green(`\n✨ ${i18n.t('wizard.language_set')}`));
56
58
  await new Promise(resolve => setTimeout(resolve, 1500));
57
59
  }
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.2",
4
+ "version": "0.4.4",
5
5
  "description": "X All In One Coding Helper",
6
6
  "author": "X.AIO",
7
7
  "homepage": "https://docs.x-aio.com/zh/docs",
@@ -33,6 +33,7 @@
33
33
  "dependencies": {
34
34
  "chalk": "^5.3.0",
35
35
  "commander": "^12.1.0",
36
+ "deepmerge-ts": "^7.1.5",
36
37
  "inquirer": "^9.2.12",
37
38
  "js-yaml": "^4.1.0",
38
39
  "open": "^11.0.0",