Skip to content

Commit 985085c

Browse files
author
catlog22
committed
Refactor CLI Config Manager and Add Provider Model Routes
- Removed deprecated constants and functions from cli-config-manager.ts. - Introduced new provider model presets in litellm-provider-models.ts for better organization and management of model information. - Created provider-routes.ts to handle API endpoints for retrieving provider information and models. - Added integration tests for provider routes to ensure correct functionality and response structure. - Implemented unit tests for settings persistence functions, covering various scenarios and edge cases. - Enhanced error handling and validation in the new routes and settings functions.
1 parent 7c16cc6 commit 985085c

13 files changed

Lines changed: 1250 additions & 298 deletions

File tree

.claude/agents/code-developer.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ output → Variable name to store this step's result
188188
```
189189
// Read task-level execution config (Single Source of Truth)
190190
const executionMethod = task.meta?.execution_config?.method || 'agent';
191-
const cliTool = task.meta?.execution_config?.cli_tool || 'codex';
191+
const cliTool = task.meta?.execution_config?.cli_tool || getDefaultCliTool(); // See ~/.claude/cli-tools.json
192192
193193
// Phase 1: Execute pre_analysis (always by Agent)
194194
const preAnalysisResults = {};
@@ -240,6 +240,13 @@ ELSE (executionMethod === 'agent'):
240240
**CLI Handoff Functions**:
241241

242242
```javascript
243+
// Get default CLI tool from cli-tools.json
244+
function getDefaultCliTool() {
245+
// Read ~/.claude/cli-tools.json and return first enabled tool
246+
// Fallback order: gemini → qwen → codex (first enabled in config)
247+
return firstEnabledTool || 'gemini'; // System default fallback
248+
}
249+
243250
// Build CLI prompt from pre-analysis results and task
244251
function buildCliHandoffPrompt(preAnalysisResults, task) {
245252
const contextSection = Object.entries(preAnalysisResults)
@@ -308,7 +315,7 @@ function buildCliCommand(task, cliTool, cliPrompt) {
308315
| Field | Values | Description |
309316
|-------|--------|-------------|
310317
| `method` | `agent` / `cli` / `hybrid` | Execution mode (default: agent) |
311-
| `cli_tool` | `codex` / `gemini` / `qwen` | CLI tool preference (default: codex) |
318+
| `cli_tool` | See `~/.claude/cli-tools.json` | CLI tool preference (first enabled tool as default) |
312319
| `enable_resume` | `true` / `false` | Enable CLI session resume |
313320
314321
**CLI Execution Reference** (from task.cli_execution):
@@ -498,7 +505,8 @@ Before completing any task, verify:
498505
- Use `run_in_background=false` for all Bash/CLI calls - agent cannot receive task hook callbacks
499506
- Set timeout ≥60 minutes for CLI commands (hooks don't propagate to subagents):
500507
```javascript
501-
Bash(command="ccw cli -p '...' --tool codex --mode write", timeout=3600000) // 60 min
508+
Bash(command="ccw cli -p '...' --tool <cli-tool> --mode write", timeout=3600000) // 60 min
509+
// <cli-tool>: First enabled tool from ~/.claude/cli-tools.json (e.g., gemini, qwen, codex)
502510
```
503511
504512
**ALWAYS:**

.claude/commands/workflow/execute.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ Task(subagent_type="{meta.agent}",
477477
- TODO List: {session.todo_list_path}
478478
- Summaries: {session.summaries_dir}
479479
480-
**Execution**: Read task JSON → Parse flow_controlExecute implementation_approach → Update TODO_LIST.md → Generate summary",
480+
**Execution**: Read task JSON → Execute pre_analysisCheck execution_config.method → (CLI: handoff to CLI tool | Agent: direct implementation) → Update TODO_LIST.md → Generate summary",
481481
description="Implement: {task.id}")
482482
```
483483

@@ -486,9 +486,11 @@ Task(subagent_type="{meta.agent}",
486486
- `[FLOW_CONTROL]`: Triggers flow_control.pre_analysis execution
487487

488488
**Why Path-Based**: Agent (code-developer.md) autonomously:
489-
- Reads and parses task JSON (requirements, acceptance, flow_control)
490-
- Loads tech stack guidelines based on detected language
491-
- Executes pre_analysis steps and implementation_approach
489+
- Reads and parses task JSON (requirements, acceptance, flow_control, execution_config)
490+
- Executes pre_analysis steps (Phase 1: context gathering)
491+
- Checks execution_config.method (Phase 2: determine mode)
492+
- CLI mode: Builds handoff prompt and executes via ccw cli with resume strategy
493+
- Agent mode: Directly implements using modification_points and logic_flow
492494
- Generates structured summary with integration points
493495

494496
Embedding task content in prompt creates duplication and conflicts with agent's parsing logic.

.claude/commands/workflow/replan.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,26 @@ const taskId = taskIdMatch?.[1]
115115
116116
4. **Parse Execution Intent** (from requirements text):
117117
```javascript
118-
// Extract execution method change from requirements
119-
const execPatterns = {
120-
cli_codex: /使用\s*(codex|Codex)\s*执行|改用\s*(codex|Codex)/i,
121-
cli_gemini: /使用\s*(gemini|Gemini)\s*执行|改用\s*(gemini|Gemini)/i,
122-
cli_qwen: /使用\s*(qwen|Qwen)\s*执行|改用\s*(qwen|Qwen)/i,
123-
agent: /改为\s*Agent\s*执行|使用\s*Agent\s*执行/i
118+
// Dynamic tool detection from cli-tools.json
119+
// Read enabled tools: ["gemini", "qwen", "codex", ...]
120+
const enabledTools = loadEnabledToolsFromConfig(); // See ~/.claude/cli-tools.json
121+
122+
// Build dynamic patterns from enabled tools
123+
function buildExecPatterns(tools) {
124+
const patterns = {
125+
agent: /改为\s*Agent\s*执行|使用\s*Agent\s*执行/i
126+
};
127+
tools.forEach(tool => {
128+
// Pattern: "使用 {tool} 执行" or "改用 {tool}"
129+
patterns[`cli_${tool}`] = new RegExp(
130+
`使用\\s*(${tool})\\s*执行|改用\\s*(${tool})`, 'i'
131+
);
132+
});
133+
return patterns;
124134
}
125135

136+
const execPatterns = buildExecPatterns(enabledTools);
137+
126138
let executionIntent = null
127139
for (const [key, pattern] of Object.entries(execPatterns)) {
128140
if (pattern.test(requirements)) {
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/**
2+
* Provider Model Presets
3+
*
4+
* Predefined model information for each supported LLM provider.
5+
* Used for UI dropdowns and validation.
6+
*/
7+
8+
import type { ProviderType } from '../types/litellm-api-config.js';
9+
10+
/**
11+
* Model information metadata
12+
*/
13+
export interface ModelInfo {
14+
/** Model identifier (used in API calls) */
15+
id: string;
16+
17+
/** Human-readable display name */
18+
name: string;
19+
20+
/** Context window size in tokens */
21+
contextWindow: number;
22+
23+
/** Whether this model supports prompt caching */
24+
supportsCaching: boolean;
25+
}
26+
27+
/**
28+
* Embedding model information metadata
29+
*/
30+
export interface EmbeddingModelInfo {
31+
/** Model identifier (used in API calls) */
32+
id: string;
33+
34+
/** Human-readable display name */
35+
name: string;
36+
37+
/** Embedding dimensions */
38+
dimensions: number;
39+
40+
/** Maximum input tokens */
41+
maxTokens: number;
42+
43+
/** Provider identifier */
44+
provider: string;
45+
}
46+
47+
48+
/**
49+
* Predefined models for each API format
50+
* Used for UI selection and validation
51+
* Note: Most providers use OpenAI-compatible format
52+
*/
53+
export const PROVIDER_MODELS: Record<ProviderType, ModelInfo[]> = {
54+
// OpenAI-compatible format (used by OpenAI, DeepSeek, Ollama, etc.)
55+
openai: [
56+
{
57+
id: 'gpt-4o',
58+
name: 'GPT-4o',
59+
contextWindow: 128000,
60+
supportsCaching: true
61+
},
62+
{
63+
id: 'gpt-4o-mini',
64+
name: 'GPT-4o Mini',
65+
contextWindow: 128000,
66+
supportsCaching: true
67+
},
68+
{
69+
id: 'o1',
70+
name: 'O1',
71+
contextWindow: 200000,
72+
supportsCaching: true
73+
},
74+
{
75+
id: 'deepseek-chat',
76+
name: 'DeepSeek Chat',
77+
contextWindow: 64000,
78+
supportsCaching: false
79+
},
80+
{
81+
id: 'deepseek-coder',
82+
name: 'DeepSeek Coder',
83+
contextWindow: 64000,
84+
supportsCaching: false
85+
},
86+
{
87+
id: 'llama3.2',
88+
name: 'Llama 3.2',
89+
contextWindow: 128000,
90+
supportsCaching: false
91+
},
92+
{
93+
id: 'qwen2.5-coder',
94+
name: 'Qwen 2.5 Coder',
95+
contextWindow: 32000,
96+
supportsCaching: false
97+
}
98+
],
99+
100+
// Anthropic format
101+
anthropic: [
102+
{
103+
id: 'claude-sonnet-4-20250514',
104+
name: 'Claude Sonnet 4',
105+
contextWindow: 200000,
106+
supportsCaching: true
107+
},
108+
{
109+
id: 'claude-3-5-sonnet-20241022',
110+
name: 'Claude 3.5 Sonnet',
111+
contextWindow: 200000,
112+
supportsCaching: true
113+
},
114+
{
115+
id: 'claude-3-5-haiku-20241022',
116+
name: 'Claude 3.5 Haiku',
117+
contextWindow: 200000,
118+
supportsCaching: true
119+
},
120+
{
121+
id: 'claude-3-opus-20240229',
122+
name: 'Claude 3 Opus',
123+
contextWindow: 200000,
124+
supportsCaching: false
125+
}
126+
],
127+
128+
// Custom format
129+
custom: [
130+
{
131+
id: 'custom-model',
132+
name: 'Custom Model',
133+
contextWindow: 128000,
134+
supportsCaching: false
135+
}
136+
]
137+
};
138+
139+
/**
140+
* Get models for a specific provider
141+
* @param providerType - Provider type to get models for
142+
* @returns Array of model information
143+
*/
144+
export function getModelsForProvider(providerType: ProviderType): ModelInfo[] {
145+
return PROVIDER_MODELS[providerType] || [];
146+
}
147+
148+
/**
149+
* Predefined embedding models for each API format
150+
* Used for UI selection and validation
151+
*/
152+
export const EMBEDDING_MODELS: Record<ProviderType, EmbeddingModelInfo[]> = {
153+
// OpenAI embedding models
154+
openai: [
155+
{
156+
id: 'text-embedding-3-small',
157+
name: 'Text Embedding 3 Small',
158+
dimensions: 1536,
159+
maxTokens: 8191,
160+
provider: 'openai'
161+
},
162+
{
163+
id: 'text-embedding-3-large',
164+
name: 'Text Embedding 3 Large',
165+
dimensions: 3072,
166+
maxTokens: 8191,
167+
provider: 'openai'
168+
},
169+
{
170+
id: 'text-embedding-ada-002',
171+
name: 'Ada 002',
172+
dimensions: 1536,
173+
maxTokens: 8191,
174+
provider: 'openai'
175+
}
176+
],
177+
178+
// Anthropic doesn't have embedding models
179+
anthropic: [],
180+
181+
// Custom embedding models
182+
custom: [
183+
{
184+
id: 'custom-embedding',
185+
name: 'Custom Embedding',
186+
dimensions: 1536,
187+
maxTokens: 8192,
188+
provider: 'custom'
189+
}
190+
]
191+
};
192+
193+
/**
194+
* Get embedding models for a specific provider
195+
* @param providerType - Provider type to get embedding models for
196+
* @returns Array of embedding model information
197+
*/
198+
export function getEmbeddingModelsForProvider(providerType: ProviderType): EmbeddingModelInfo[] {
199+
return EMBEDDING_MODELS[providerType] || [];
200+
}
201+
202+
203+
/**
204+
* Get model information by ID within a provider
205+
* @param providerType - Provider type
206+
* @param modelId - Model identifier
207+
* @returns Model information or undefined if not found
208+
*/
209+
export function getModelInfo(providerType: ProviderType, modelId: string): ModelInfo | undefined {
210+
const models = PROVIDER_MODELS[providerType] || [];
211+
return models.find(m => m.id === modelId);
212+
}
213+
214+
/**
215+
* Validate if a model ID is supported by a provider
216+
* @param providerType - Provider type
217+
* @param modelId - Model identifier to validate
218+
* @returns true if model is valid for provider
219+
*/
220+
export function isValidModel(providerType: ProviderType, modelId: string): boolean {
221+
return getModelInfo(providerType, modelId) !== undefined;
222+
}

0 commit comments

Comments
 (0)