claude-code-workflow 6.2.7 → 6.3.0
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.
- package/.claude/CLAUDE.md +16 -1
- package/.claude/workflows/cli-templates/protocols/analysis-protocol.md +11 -4
- package/.claude/workflows/cli-templates/protocols/write-protocol.md +10 -75
- package/.claude/workflows/cli-tools-usage.md +14 -24
- package/.codex/AGENTS.md +51 -1
- package/.codex/prompts/compact.md +378 -0
- package/.gemini/GEMINI.md +57 -20
- package/ccw/dist/cli.d.ts.map +1 -1
- package/ccw/dist/cli.js +21 -8
- package/ccw/dist/cli.js.map +1 -1
- package/ccw/dist/commands/cli.d.ts +2 -0
- package/ccw/dist/commands/cli.d.ts.map +1 -1
- package/ccw/dist/commands/cli.js +129 -8
- package/ccw/dist/commands/cli.js.map +1 -1
- package/ccw/dist/commands/hook.d.ts.map +1 -1
- package/ccw/dist/commands/hook.js +3 -2
- package/ccw/dist/commands/hook.js.map +1 -1
- package/ccw/dist/config/litellm-api-config-manager.d.ts +180 -0
- package/ccw/dist/config/litellm-api-config-manager.d.ts.map +1 -0
- package/ccw/dist/config/litellm-api-config-manager.js +770 -0
- package/ccw/dist/config/litellm-api-config-manager.js.map +1 -0
- package/ccw/dist/config/provider-models.d.ts +73 -0
- package/ccw/dist/config/provider-models.d.ts.map +1 -0
- package/ccw/dist/config/provider-models.js +172 -0
- package/ccw/dist/config/provider-models.js.map +1 -0
- package/ccw/dist/core/cache-manager.d.ts.map +1 -1
- package/ccw/dist/core/cache-manager.js +3 -5
- package/ccw/dist/core/cache-manager.js.map +1 -1
- package/ccw/dist/core/dashboard-generator.d.ts.map +1 -1
- package/ccw/dist/core/dashboard-generator.js +3 -1
- package/ccw/dist/core/dashboard-generator.js.map +1 -1
- package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/cli-routes.js +169 -0
- package/ccw/dist/core/routes/cli-routes.js.map +1 -1
- package/ccw/dist/core/routes/codexlens-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/codexlens-routes.js +234 -18
- package/ccw/dist/core/routes/codexlens-routes.js.map +1 -1
- package/ccw/dist/core/routes/hooks-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/hooks-routes.js +30 -32
- package/ccw/dist/core/routes/hooks-routes.js.map +1 -1
- package/ccw/dist/core/routes/litellm-api-routes.d.ts +21 -0
- package/ccw/dist/core/routes/litellm-api-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/litellm-api-routes.js +780 -0
- package/ccw/dist/core/routes/litellm-api-routes.js.map +1 -0
- package/ccw/dist/core/routes/litellm-routes.d.ts +20 -0
- package/ccw/dist/core/routes/litellm-routes.d.ts.map +1 -0
- package/ccw/dist/core/routes/litellm-routes.js +85 -0
- package/ccw/dist/core/routes/litellm-routes.js.map +1 -0
- package/ccw/dist/core/routes/mcp-routes.js +2 -2
- package/ccw/dist/core/routes/mcp-routes.js.map +1 -1
- package/ccw/dist/core/routes/status-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/status-routes.js +39 -0
- package/ccw/dist/core/routes/status-routes.js.map +1 -1
- package/ccw/dist/core/routes/system-routes.js +1 -1
- package/ccw/dist/core/routes/system-routes.js.map +1 -1
- package/ccw/dist/core/server.d.ts.map +1 -1
- package/ccw/dist/core/server.js +15 -1
- package/ccw/dist/core/server.js.map +1 -1
- package/ccw/dist/mcp-server/index.js +1 -1
- package/ccw/dist/mcp-server/index.js.map +1 -1
- package/ccw/dist/tools/claude-cli-tools.d.ts +82 -0
- package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -0
- package/ccw/dist/tools/claude-cli-tools.js +216 -0
- package/ccw/dist/tools/claude-cli-tools.js.map +1 -0
- package/ccw/dist/tools/cli-executor.d.ts.map +1 -1
- package/ccw/dist/tools/cli-executor.js +76 -14
- package/ccw/dist/tools/cli-executor.js.map +1 -1
- package/ccw/dist/tools/codex-lens.d.ts +9 -2
- package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
- package/ccw/dist/tools/codex-lens.js +114 -9
- package/ccw/dist/tools/codex-lens.js.map +1 -1
- package/ccw/dist/tools/context-cache-store.d.ts +136 -0
- package/ccw/dist/tools/context-cache-store.d.ts.map +1 -0
- package/ccw/dist/tools/context-cache-store.js +256 -0
- package/ccw/dist/tools/context-cache-store.js.map +1 -0
- package/ccw/dist/tools/context-cache.d.ts +56 -0
- package/ccw/dist/tools/context-cache.d.ts.map +1 -0
- package/ccw/dist/tools/context-cache.js +294 -0
- package/ccw/dist/tools/context-cache.js.map +1 -0
- package/ccw/dist/tools/core-memory.d.ts.map +1 -1
- package/ccw/dist/tools/core-memory.js +33 -19
- package/ccw/dist/tools/core-memory.js.map +1 -1
- package/ccw/dist/tools/index.d.ts.map +1 -1
- package/ccw/dist/tools/index.js +2 -0
- package/ccw/dist/tools/index.js.map +1 -1
- package/ccw/dist/tools/litellm-client.d.ts +85 -0
- package/ccw/dist/tools/litellm-client.d.ts.map +1 -0
- package/ccw/dist/tools/litellm-client.js +188 -0
- package/ccw/dist/tools/litellm-client.js.map +1 -0
- package/ccw/dist/tools/litellm-executor.d.ts +34 -0
- package/ccw/dist/tools/litellm-executor.d.ts.map +1 -0
- package/ccw/dist/tools/litellm-executor.js +192 -0
- package/ccw/dist/tools/litellm-executor.js.map +1 -0
- package/ccw/dist/tools/pattern-parser.d.ts +55 -0
- package/ccw/dist/tools/pattern-parser.d.ts.map +1 -0
- package/ccw/dist/tools/pattern-parser.js +237 -0
- package/ccw/dist/tools/pattern-parser.js.map +1 -0
- package/ccw/dist/tools/smart-search.d.ts +1 -0
- package/ccw/dist/tools/smart-search.d.ts.map +1 -1
- package/ccw/dist/tools/smart-search.js +117 -41
- package/ccw/dist/tools/smart-search.js.map +1 -1
- package/ccw/dist/types/litellm-api-config.d.ts +294 -0
- package/ccw/dist/types/litellm-api-config.d.ts.map +1 -0
- package/ccw/dist/types/litellm-api-config.js +8 -0
- package/ccw/dist/types/litellm-api-config.js.map +1 -0
- package/ccw/src/cli.ts +258 -244
- package/ccw/src/commands/cli.ts +153 -9
- package/ccw/src/commands/hook.ts +3 -2
- package/ccw/src/config/.litellm-api-config-manager.ts.2025-12-23T11-57-43-727Z.bak +441 -0
- package/ccw/src/config/litellm-api-config-manager.ts +1012 -0
- package/ccw/src/config/provider-models.ts +222 -0
- package/ccw/src/core/cache-manager.ts +292 -294
- package/ccw/src/core/dashboard-generator.ts +3 -1
- package/ccw/src/core/routes/cli-routes.ts +192 -0
- package/ccw/src/core/routes/codexlens-routes.ts +241 -19
- package/ccw/src/core/routes/hooks-routes.ts +399 -405
- package/ccw/src/core/routes/litellm-api-routes.ts +930 -0
- package/ccw/src/core/routes/litellm-routes.ts +107 -0
- package/ccw/src/core/routes/mcp-routes.ts +1271 -1271
- package/ccw/src/core/routes/status-routes.ts +51 -0
- package/ccw/src/core/routes/system-routes.ts +1 -1
- package/ccw/src/core/server.ts +15 -1
- package/ccw/src/mcp-server/index.ts +1 -1
- package/ccw/src/templates/dashboard-css/12-cli-legacy.css +44 -0
- package/ccw/src/templates/dashboard-css/31-api-settings.css +2265 -0
- package/ccw/src/templates/dashboard-js/components/cli-history.js +15 -8
- package/ccw/src/templates/dashboard-js/components/cli-status.js +323 -9
- package/ccw/src/templates/dashboard-js/components/navigation.js +329 -313
- package/ccw/src/templates/dashboard-js/i18n.js +583 -1
- package/ccw/src/templates/dashboard-js/views/api-settings.js +3362 -0
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +199 -24
- package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +1265 -27
- package/ccw/src/templates/dashboard.html +840 -831
- package/ccw/src/tools/claude-cli-tools.ts +300 -0
- package/ccw/src/tools/cli-executor.ts +83 -14
- package/ccw/src/tools/codex-lens.ts +146 -9
- package/ccw/src/tools/context-cache-store.ts +368 -0
- package/ccw/src/tools/context-cache.ts +393 -0
- package/ccw/src/tools/core-memory.ts +33 -19
- package/ccw/src/tools/index.ts +2 -0
- package/ccw/src/tools/litellm-client.ts +246 -0
- package/ccw/src/tools/litellm-executor.ts +241 -0
- package/ccw/src/tools/pattern-parser.ts +329 -0
- package/ccw/src/tools/smart-search.ts +142 -41
- package/ccw/src/types/litellm-api-config.ts +402 -0
- package/ccw-litellm/README.md +180 -0
- package/ccw-litellm/pyproject.toml +35 -0
- package/ccw-litellm/src/ccw_litellm/__init__.py +47 -0
- package/ccw-litellm/src/ccw_litellm/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/__pycache__/cli.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/cli.py +108 -0
- package/ccw-litellm/src/ccw_litellm/clients/__init__.py +12 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_embedder.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_llm.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/clients/litellm_embedder.py +251 -0
- package/ccw-litellm/src/ccw_litellm/clients/litellm_llm.py +165 -0
- package/ccw-litellm/src/ccw_litellm/config/__init__.py +22 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/loader.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/__pycache__/models.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/config/loader.py +316 -0
- package/ccw-litellm/src/ccw_litellm/config/models.py +130 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__init__.py +14 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/__init__.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/embedder.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/llm.cpython-313.pyc +0 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/embedder.py +52 -0
- package/ccw-litellm/src/ccw_litellm/interfaces/llm.py +45 -0
- package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/__pycache__/output.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/cli/commands.py +378 -23
- package/codex-lens/src/codexlens/cli/embedding_manager.py +660 -56
- package/codex-lens/src/codexlens/cli/model_manager.py +31 -18
- package/codex-lens/src/codexlens/cli/output.py +12 -1
- package/codex-lens/src/codexlens/config.py +93 -0
- package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/search/chain_search.py +6 -2
- package/codex-lens/src/codexlens/search/hybrid_search.py +44 -21
- package/codex-lens/src/codexlens/search/ranking.py +1 -1
- package/codex-lens/src/codexlens/semantic/__init__.py +42 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/base.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/factory.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/gpu_support.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/litellm_embedder.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/semantic/base.py +61 -0
- package/codex-lens/src/codexlens/semantic/chunker.py +43 -20
- package/codex-lens/src/codexlens/semantic/embedder.py +60 -13
- package/codex-lens/src/codexlens/semantic/factory.py +98 -0
- package/codex-lens/src/codexlens/semantic/gpu_support.py +225 -3
- package/codex-lens/src/codexlens/semantic/litellm_embedder.py +144 -0
- package/codex-lens/src/codexlens/semantic/rotational_embedder.py +434 -0
- package/codex-lens/src/codexlens/semantic/vector_store.py +33 -8
- package/codex-lens/src/codexlens/storage/__pycache__/path_mapper.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_004_dual_fts.cpython-313.pyc +0 -0
- package/codex-lens/src/codexlens/storage/path_mapper.py +27 -1
- package/package.json +15 -5
- package/.codex/prompts.zip +0 -0
- package/ccw/package.json +0 -65
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LiteLLM Client - Bridge between CCW and ccw-litellm Python package
|
|
3
|
+
* Provides LLM chat and embedding capabilities via spawned Python process
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Chat completions with multiple models
|
|
7
|
+
* - Text embeddings generation
|
|
8
|
+
* - Configuration management
|
|
9
|
+
* - JSON protocol communication
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { spawn } from 'child_process';
|
|
13
|
+
import { promisify } from 'util';
|
|
14
|
+
|
|
15
|
+
export interface LiteLLMConfig {
|
|
16
|
+
pythonPath?: string; // Default 'python'
|
|
17
|
+
configPath?: string; // Configuration file path
|
|
18
|
+
timeout?: number; // Default 60000ms
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ChatMessage {
|
|
22
|
+
role: 'system' | 'user' | 'assistant';
|
|
23
|
+
content: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ChatResponse {
|
|
27
|
+
content: string;
|
|
28
|
+
model: string;
|
|
29
|
+
usage?: {
|
|
30
|
+
prompt_tokens: number;
|
|
31
|
+
completion_tokens: number;
|
|
32
|
+
total_tokens: number;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface EmbedResponse {
|
|
37
|
+
vectors: number[][];
|
|
38
|
+
dimensions: number;
|
|
39
|
+
model: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface LiteLLMStatus {
|
|
43
|
+
available: boolean;
|
|
44
|
+
version?: string;
|
|
45
|
+
error?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class LiteLLMClient {
|
|
49
|
+
private pythonPath: string;
|
|
50
|
+
private configPath?: string;
|
|
51
|
+
private timeout: number;
|
|
52
|
+
|
|
53
|
+
constructor(config: LiteLLMConfig = {}) {
|
|
54
|
+
this.pythonPath = config.pythonPath || 'python';
|
|
55
|
+
this.configPath = config.configPath;
|
|
56
|
+
this.timeout = config.timeout || 60000;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Execute Python ccw-litellm command
|
|
61
|
+
*/
|
|
62
|
+
private async executePython(args: string[], options: { timeout?: number } = {}): Promise<string> {
|
|
63
|
+
const timeout = options.timeout || this.timeout;
|
|
64
|
+
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
const proc = spawn(this.pythonPath, ['-m', 'ccw_litellm.cli', ...args], {
|
|
67
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
68
|
+
env: { ...process.env }
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
let stdout = '';
|
|
72
|
+
let stderr = '';
|
|
73
|
+
let timedOut = false;
|
|
74
|
+
|
|
75
|
+
// Set up timeout
|
|
76
|
+
const timeoutId = setTimeout(() => {
|
|
77
|
+
timedOut = true;
|
|
78
|
+
proc.kill('SIGTERM');
|
|
79
|
+
reject(new Error(`Command timed out after ${timeout}ms`));
|
|
80
|
+
}, timeout);
|
|
81
|
+
|
|
82
|
+
proc.stdout.on('data', (data) => {
|
|
83
|
+
stdout += data.toString();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
proc.stderr.on('data', (data) => {
|
|
87
|
+
stderr += data.toString();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
proc.on('error', (error) => {
|
|
91
|
+
clearTimeout(timeoutId);
|
|
92
|
+
reject(new Error(`Failed to spawn Python process: ${error.message}`));
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
proc.on('close', (code) => {
|
|
96
|
+
clearTimeout(timeoutId);
|
|
97
|
+
|
|
98
|
+
if (timedOut) {
|
|
99
|
+
return; // Already rejected
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (code === 0) {
|
|
103
|
+
resolve(stdout.trim());
|
|
104
|
+
} else {
|
|
105
|
+
const errorMsg = stderr.trim() || `Process exited with code ${code}`;
|
|
106
|
+
reject(new Error(errorMsg));
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check if ccw-litellm is available
|
|
114
|
+
*/
|
|
115
|
+
async isAvailable(): Promise<boolean> {
|
|
116
|
+
try {
|
|
117
|
+
await this.executePython(['version'], { timeout: 5000 });
|
|
118
|
+
return true;
|
|
119
|
+
} catch {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get status information
|
|
126
|
+
*/
|
|
127
|
+
async getStatus(): Promise<LiteLLMStatus> {
|
|
128
|
+
try {
|
|
129
|
+
const output = await this.executePython(['version'], { timeout: 5000 });
|
|
130
|
+
return {
|
|
131
|
+
available: true,
|
|
132
|
+
version: output.trim()
|
|
133
|
+
};
|
|
134
|
+
} catch (error: any) {
|
|
135
|
+
return {
|
|
136
|
+
available: false,
|
|
137
|
+
error: error.message
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get current configuration
|
|
144
|
+
*/
|
|
145
|
+
async getConfig(): Promise<any> {
|
|
146
|
+
const output = await this.executePython(['config', '--json']);
|
|
147
|
+
return JSON.parse(output);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Generate embeddings for texts
|
|
152
|
+
*/
|
|
153
|
+
async embed(texts: string[], model: string = 'default'): Promise<EmbedResponse> {
|
|
154
|
+
if (!texts || texts.length === 0) {
|
|
155
|
+
throw new Error('texts array cannot be empty');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const args = ['embed', '--model', model, '--output', 'json'];
|
|
159
|
+
|
|
160
|
+
// Add texts as arguments
|
|
161
|
+
for (const text of texts) {
|
|
162
|
+
args.push(text);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const output = await this.executePython(args, { timeout: this.timeout * 2 });
|
|
166
|
+
const vectors = JSON.parse(output);
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
vectors,
|
|
170
|
+
dimensions: vectors[0]?.length || 0,
|
|
171
|
+
model
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Chat with LLM
|
|
177
|
+
*/
|
|
178
|
+
async chat(message: string, model: string = 'default'): Promise<string> {
|
|
179
|
+
if (!message) {
|
|
180
|
+
throw new Error('message cannot be empty');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const args = ['chat', '--model', model, message];
|
|
184
|
+
return this.executePython(args, { timeout: this.timeout * 2 });
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Multi-turn chat with messages array
|
|
189
|
+
*/
|
|
190
|
+
async chatMessages(messages: ChatMessage[], model: string = 'default'): Promise<ChatResponse> {
|
|
191
|
+
if (!messages || messages.length === 0) {
|
|
192
|
+
throw new Error('messages array cannot be empty');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// For now, just use the last user message
|
|
196
|
+
// TODO: Implement full message history support in ccw-litellm
|
|
197
|
+
const lastMessage = messages[messages.length - 1];
|
|
198
|
+
const content = await this.chat(lastMessage.content, model);
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
content,
|
|
202
|
+
model,
|
|
203
|
+
usage: undefined // TODO: Add usage tracking
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Singleton instance
|
|
209
|
+
let _client: LiteLLMClient | null = null;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get or create singleton LiteLLM client
|
|
213
|
+
*/
|
|
214
|
+
export function getLiteLLMClient(config?: LiteLLMConfig): LiteLLMClient {
|
|
215
|
+
if (!_client) {
|
|
216
|
+
_client = new LiteLLMClient(config);
|
|
217
|
+
}
|
|
218
|
+
return _client;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Check if LiteLLM is available
|
|
223
|
+
*/
|
|
224
|
+
export async function checkLiteLLMAvailable(): Promise<boolean> {
|
|
225
|
+
try {
|
|
226
|
+
const client = getLiteLLMClient();
|
|
227
|
+
return await client.isAvailable();
|
|
228
|
+
} catch {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Get LiteLLM status
|
|
235
|
+
*/
|
|
236
|
+
export async function getLiteLLMStatus(): Promise<LiteLLMStatus> {
|
|
237
|
+
try {
|
|
238
|
+
const client = getLiteLLMClient();
|
|
239
|
+
return await client.getStatus();
|
|
240
|
+
} catch (error: any) {
|
|
241
|
+
return {
|
|
242
|
+
available: false,
|
|
243
|
+
error: error.message
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LiteLLM Executor - Execute LiteLLM endpoints with context caching
|
|
3
|
+
* Integrates with context-cache for file packing and LiteLLM client for API calls
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getLiteLLMClient } from './litellm-client.js';
|
|
7
|
+
import { handler as contextCacheHandler } from './context-cache.js';
|
|
8
|
+
import {
|
|
9
|
+
findEndpointById,
|
|
10
|
+
getProviderWithResolvedEnvVars,
|
|
11
|
+
} from '../config/litellm-api-config-manager.js';
|
|
12
|
+
import type { CustomEndpoint, ProviderCredential } from '../types/litellm-api-config.js';
|
|
13
|
+
|
|
14
|
+
export interface LiteLLMExecutionOptions {
|
|
15
|
+
prompt: string;
|
|
16
|
+
endpointId: string; // Custom endpoint ID (e.g., "my-gpt4o")
|
|
17
|
+
baseDir: string; // Project base directory
|
|
18
|
+
cwd?: string; // Working directory for file resolution
|
|
19
|
+
includeDirs?: string[]; // Additional directories for @patterns
|
|
20
|
+
enableCache?: boolean; // Override endpoint cache setting
|
|
21
|
+
onOutput?: (data: { type: string; data: string }) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface LiteLLMExecutionResult {
|
|
25
|
+
success: boolean;
|
|
26
|
+
output: string;
|
|
27
|
+
model: string;
|
|
28
|
+
provider: string;
|
|
29
|
+
cacheUsed: boolean;
|
|
30
|
+
cachedFiles?: string[];
|
|
31
|
+
error?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Extract @patterns from prompt text
|
|
36
|
+
*/
|
|
37
|
+
export function extractPatterns(prompt: string): string[] {
|
|
38
|
+
// Match @path patterns: @src/**/*.ts, @CLAUDE.md, @../shared/**/*
|
|
39
|
+
const regex = /@([^\s]+)/g;
|
|
40
|
+
const patterns: string[] = [];
|
|
41
|
+
let match;
|
|
42
|
+
while ((match = regex.exec(prompt)) !== null) {
|
|
43
|
+
patterns.push('@' + match[1]);
|
|
44
|
+
}
|
|
45
|
+
return patterns;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Execute LiteLLM endpoint with optional context caching
|
|
50
|
+
*/
|
|
51
|
+
export async function executeLiteLLMEndpoint(
|
|
52
|
+
options: LiteLLMExecutionOptions
|
|
53
|
+
): Promise<LiteLLMExecutionResult> {
|
|
54
|
+
const { prompt, endpointId, baseDir, cwd, includeDirs, enableCache, onOutput } = options;
|
|
55
|
+
|
|
56
|
+
// 1. Find endpoint configuration
|
|
57
|
+
const endpoint = findEndpointById(baseDir, endpointId);
|
|
58
|
+
if (!endpoint) {
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
output: '',
|
|
62
|
+
model: '',
|
|
63
|
+
provider: '',
|
|
64
|
+
cacheUsed: false,
|
|
65
|
+
error: `Endpoint not found: ${endpointId}`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 2. Get provider with resolved env vars
|
|
70
|
+
const provider = getProviderWithResolvedEnvVars(baseDir, endpoint.providerId);
|
|
71
|
+
if (!provider) {
|
|
72
|
+
return {
|
|
73
|
+
success: false,
|
|
74
|
+
output: '',
|
|
75
|
+
model: '',
|
|
76
|
+
provider: '',
|
|
77
|
+
cacheUsed: false,
|
|
78
|
+
error: `Provider not found: ${endpoint.providerId}`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Verify API key is available
|
|
83
|
+
if (!provider.resolvedApiKey) {
|
|
84
|
+
return {
|
|
85
|
+
success: false,
|
|
86
|
+
output: '',
|
|
87
|
+
model: endpoint.model,
|
|
88
|
+
provider: provider.type,
|
|
89
|
+
cacheUsed: false,
|
|
90
|
+
error: `API key not configured for provider: ${provider.name}`,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 3. Process context cache if enabled
|
|
95
|
+
let finalPrompt = prompt;
|
|
96
|
+
let cacheUsed = false;
|
|
97
|
+
let cachedFiles: string[] = [];
|
|
98
|
+
|
|
99
|
+
const shouldCache = enableCache ?? endpoint.cacheStrategy.enabled;
|
|
100
|
+
if (shouldCache) {
|
|
101
|
+
const patterns = extractPatterns(prompt);
|
|
102
|
+
if (patterns.length > 0) {
|
|
103
|
+
if (onOutput) {
|
|
104
|
+
onOutput({ type: 'stderr', data: `[Context cache: Found ${patterns.length} @patterns]\n` });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Pack files into cache
|
|
108
|
+
const packResult = await contextCacheHandler({
|
|
109
|
+
operation: 'pack',
|
|
110
|
+
patterns,
|
|
111
|
+
cwd: cwd || process.cwd(),
|
|
112
|
+
include_dirs: includeDirs,
|
|
113
|
+
ttl: endpoint.cacheStrategy.ttlMinutes * 60 * 1000,
|
|
114
|
+
max_file_size: endpoint.cacheStrategy.maxSizeKB * 1024,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (packResult.success && packResult.result) {
|
|
118
|
+
const pack = packResult.result as any;
|
|
119
|
+
|
|
120
|
+
if (onOutput) {
|
|
121
|
+
onOutput({
|
|
122
|
+
type: 'stderr',
|
|
123
|
+
data: `[Context cache: Packed ${pack.files_packed} files, ${pack.total_bytes} bytes]\n`,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Read cached content
|
|
128
|
+
const readResult = await contextCacheHandler({
|
|
129
|
+
operation: 'read',
|
|
130
|
+
session_id: pack.session_id,
|
|
131
|
+
limit: endpoint.cacheStrategy.maxSizeKB * 1024,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (readResult.success && readResult.result) {
|
|
135
|
+
const read = readResult.result as any;
|
|
136
|
+
// Prepend cached content to prompt
|
|
137
|
+
finalPrompt = `${read.content}\n\n---\n\n${prompt}`;
|
|
138
|
+
cacheUsed = true;
|
|
139
|
+
cachedFiles = pack.files_packed ? Array(pack.files_packed).fill('...') : [];
|
|
140
|
+
|
|
141
|
+
if (onOutput) {
|
|
142
|
+
onOutput({ type: 'stderr', data: `[Context cache: Applied to prompt]\n` });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} else if (packResult.error) {
|
|
146
|
+
if (onOutput) {
|
|
147
|
+
onOutput({ type: 'stderr', data: `[Context cache warning: ${packResult.error}]\n` });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 4. Call LiteLLM
|
|
154
|
+
try {
|
|
155
|
+
if (onOutput) {
|
|
156
|
+
onOutput({
|
|
157
|
+
type: 'stderr',
|
|
158
|
+
data: `[LiteLLM: Calling ${provider.type}/${endpoint.model}]\n`,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const client = getLiteLLMClient({
|
|
163
|
+
pythonPath: 'python',
|
|
164
|
+
timeout: 120000, // 2 minutes
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Configure provider credentials via environment
|
|
168
|
+
// LiteLLM uses standard env vars like OPENAI_API_KEY, ANTHROPIC_API_KEY
|
|
169
|
+
const envVarName = getProviderEnvVarName(provider.type);
|
|
170
|
+
if (envVarName) {
|
|
171
|
+
process.env[envVarName] = provider.resolvedApiKey;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Set base URL if custom
|
|
175
|
+
if (provider.apiBase) {
|
|
176
|
+
const baseUrlEnvVar = getProviderBaseUrlEnvVarName(provider.type);
|
|
177
|
+
if (baseUrlEnvVar) {
|
|
178
|
+
process.env[baseUrlEnvVar] = provider.apiBase;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Use litellm-client to call chat
|
|
183
|
+
const response = await client.chat(finalPrompt, endpoint.model);
|
|
184
|
+
|
|
185
|
+
if (onOutput) {
|
|
186
|
+
onOutput({ type: 'stdout', data: response });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
success: true,
|
|
191
|
+
output: response,
|
|
192
|
+
model: endpoint.model,
|
|
193
|
+
provider: provider.type,
|
|
194
|
+
cacheUsed,
|
|
195
|
+
cachedFiles,
|
|
196
|
+
};
|
|
197
|
+
} catch (error) {
|
|
198
|
+
const errorMsg = (error as Error).message;
|
|
199
|
+
if (onOutput) {
|
|
200
|
+
onOutput({ type: 'stderr', data: `[LiteLLM error: ${errorMsg}]\n` });
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
success: false,
|
|
205
|
+
output: '',
|
|
206
|
+
model: endpoint.model,
|
|
207
|
+
provider: provider.type,
|
|
208
|
+
cacheUsed,
|
|
209
|
+
error: errorMsg,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Get environment variable name for provider API key
|
|
216
|
+
*/
|
|
217
|
+
function getProviderEnvVarName(providerType: string): string | null {
|
|
218
|
+
const envVarMap: Record<string, string> = {
|
|
219
|
+
openai: 'OPENAI_API_KEY',
|
|
220
|
+
anthropic: 'ANTHROPIC_API_KEY',
|
|
221
|
+
google: 'GOOGLE_API_KEY',
|
|
222
|
+
azure: 'AZURE_API_KEY',
|
|
223
|
+
mistral: 'MISTRAL_API_KEY',
|
|
224
|
+
deepseek: 'DEEPSEEK_API_KEY',
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
return envVarMap[providerType] || null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get environment variable name for provider base URL
|
|
232
|
+
*/
|
|
233
|
+
function getProviderBaseUrlEnvVarName(providerType: string): string | null {
|
|
234
|
+
const envVarMap: Record<string, string> = {
|
|
235
|
+
openai: 'OPENAI_API_BASE',
|
|
236
|
+
anthropic: 'ANTHROPIC_API_BASE',
|
|
237
|
+
azure: 'AZURE_API_BASE',
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
return envVarMap[providerType] || null;
|
|
241
|
+
}
|