@vibeframe/cli 0.27.0 → 0.29.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/LICENSE +21 -0
- package/dist/agent/adapters/index.d.ts +1 -0
- package/dist/agent/adapters/index.d.ts.map +1 -1
- package/dist/agent/adapters/index.js +5 -0
- package/dist/agent/adapters/index.js.map +1 -1
- package/dist/agent/adapters/openrouter.d.ts +16 -0
- package/dist/agent/adapters/openrouter.d.ts.map +1 -0
- package/dist/agent/adapters/openrouter.js +100 -0
- package/dist/agent/adapters/openrouter.js.map +1 -0
- package/dist/agent/types.d.ts +1 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +3 -1
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/setup.js +5 -2
- package/dist/commands/setup.js.map +1 -1
- package/dist/config/schema.d.ts +2 -1
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +2 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/index.js +0 -0
- package/package.json +16 -12
- package/.turbo/turbo-build.log +0 -4
- package/.turbo/turbo-lint.log +0 -21
- package/.turbo/turbo-test.log +0 -689
- package/src/agent/adapters/claude.ts +0 -143
- package/src/agent/adapters/gemini.ts +0 -159
- package/src/agent/adapters/index.ts +0 -61
- package/src/agent/adapters/ollama.ts +0 -231
- package/src/agent/adapters/openai.ts +0 -116
- package/src/agent/adapters/xai.ts +0 -119
- package/src/agent/index.ts +0 -251
- package/src/agent/memory/index.ts +0 -151
- package/src/agent/prompts/system.ts +0 -106
- package/src/agent/tools/ai-editing.ts +0 -845
- package/src/agent/tools/ai-generation.ts +0 -1073
- package/src/agent/tools/ai-pipeline.ts +0 -1055
- package/src/agent/tools/ai.ts +0 -21
- package/src/agent/tools/batch.ts +0 -429
- package/src/agent/tools/e2e.test.ts +0 -545
- package/src/agent/tools/export.ts +0 -184
- package/src/agent/tools/filesystem.ts +0 -237
- package/src/agent/tools/index.ts +0 -150
- package/src/agent/tools/integration.test.ts +0 -775
- package/src/agent/tools/media.ts +0 -697
- package/src/agent/tools/project.ts +0 -313
- package/src/agent/tools/timeline.ts +0 -951
- package/src/agent/types.ts +0 -68
- package/src/commands/agent.ts +0 -340
- package/src/commands/ai-analyze.ts +0 -429
- package/src/commands/ai-animated-caption.ts +0 -390
- package/src/commands/ai-audio.ts +0 -941
- package/src/commands/ai-broll.ts +0 -490
- package/src/commands/ai-edit-cli.ts +0 -658
- package/src/commands/ai-edit.ts +0 -1542
- package/src/commands/ai-fill-gaps.ts +0 -566
- package/src/commands/ai-helpers.ts +0 -65
- package/src/commands/ai-highlights.ts +0 -1303
- package/src/commands/ai-image.ts +0 -761
- package/src/commands/ai-motion.ts +0 -347
- package/src/commands/ai-narrate.ts +0 -451
- package/src/commands/ai-review.ts +0 -309
- package/src/commands/ai-script-pipeline-cli.ts +0 -1710
- package/src/commands/ai-script-pipeline.ts +0 -1365
- package/src/commands/ai-suggest-edit.ts +0 -264
- package/src/commands/ai-video-fx.ts +0 -445
- package/src/commands/ai-video.ts +0 -915
- package/src/commands/ai-viral.ts +0 -595
- package/src/commands/ai-visual-fx.ts +0 -601
- package/src/commands/ai.test.ts +0 -627
- package/src/commands/ai.ts +0 -307
- package/src/commands/analyze.ts +0 -282
- package/src/commands/audio.ts +0 -644
- package/src/commands/batch.test.ts +0 -279
- package/src/commands/batch.ts +0 -440
- package/src/commands/detect.ts +0 -329
- package/src/commands/doctor.ts +0 -237
- package/src/commands/edit-cmd.ts +0 -1014
- package/src/commands/export.ts +0 -918
- package/src/commands/generate.ts +0 -2146
- package/src/commands/media.ts +0 -177
- package/src/commands/output.ts +0 -142
- package/src/commands/pipeline.ts +0 -398
- package/src/commands/project.test.ts +0 -127
- package/src/commands/project.ts +0 -149
- package/src/commands/sanitize.ts +0 -60
- package/src/commands/schema.ts +0 -130
- package/src/commands/setup.ts +0 -509
- package/src/commands/timeline.test.ts +0 -499
- package/src/commands/timeline.ts +0 -529
- package/src/commands/validate.ts +0 -77
- package/src/config/config.test.ts +0 -197
- package/src/config/index.ts +0 -125
- package/src/config/schema.ts +0 -82
- package/src/engine/index.ts +0 -2
- package/src/engine/project.test.ts +0 -702
- package/src/engine/project.ts +0 -439
- package/src/index.ts +0 -146
- package/src/utils/api-key.test.ts +0 -41
- package/src/utils/api-key.ts +0 -247
- package/src/utils/audio.ts +0 -83
- package/src/utils/exec-safe.ts +0 -75
- package/src/utils/first-run.ts +0 -52
- package/src/utils/provider-resolver.ts +0 -56
- package/src/utils/remotion.ts +0 -951
- package/src/utils/subtitle.test.ts +0 -227
- package/src/utils/subtitle.ts +0 -169
- package/src/utils/tty.ts +0 -196
- package/tsconfig.json +0 -20
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Claude LLM Adapter with tool_use
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import Anthropic from "@anthropic-ai/sdk";
|
|
6
|
-
import type { LLMAdapter } from "./index.js";
|
|
7
|
-
import type {
|
|
8
|
-
ToolDefinition,
|
|
9
|
-
LLMResponse,
|
|
10
|
-
AgentMessage,
|
|
11
|
-
ToolCall,
|
|
12
|
-
LLMProvider,
|
|
13
|
-
} from "../types.js";
|
|
14
|
-
|
|
15
|
-
export class ClaudeAdapter implements LLMAdapter {
|
|
16
|
-
readonly provider: LLMProvider = "claude";
|
|
17
|
-
private client: Anthropic | null = null;
|
|
18
|
-
private model: string = "claude-sonnet-4-6";
|
|
19
|
-
|
|
20
|
-
async initialize(apiKey: string): Promise<void> {
|
|
21
|
-
this.client = new Anthropic({ apiKey });
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
isInitialized(): boolean {
|
|
25
|
-
return this.client !== null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
setModel(model: string): void {
|
|
29
|
-
this.model = model;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async chat(
|
|
33
|
-
messages: AgentMessage[],
|
|
34
|
-
tools: ToolDefinition[]
|
|
35
|
-
): Promise<LLMResponse> {
|
|
36
|
-
if (!this.client) {
|
|
37
|
-
throw new Error("Claude adapter not initialized");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Extract system message
|
|
41
|
-
const systemMessage = messages.find((m) => m.role === "system");
|
|
42
|
-
const systemPrompt = systemMessage?.content || "";
|
|
43
|
-
|
|
44
|
-
// Convert messages to Claude format
|
|
45
|
-
const claudeMessages: Anthropic.MessageParam[] = [];
|
|
46
|
-
|
|
47
|
-
for (const msg of messages) {
|
|
48
|
-
if (msg.role === "system") continue;
|
|
49
|
-
|
|
50
|
-
if (msg.role === "user") {
|
|
51
|
-
claudeMessages.push({
|
|
52
|
-
role: "user",
|
|
53
|
-
content: msg.content,
|
|
54
|
-
});
|
|
55
|
-
} else if (msg.role === "assistant") {
|
|
56
|
-
const content: Anthropic.ContentBlockParam[] = [];
|
|
57
|
-
|
|
58
|
-
if (msg.content) {
|
|
59
|
-
content.push({ type: "text", text: msg.content });
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (msg.toolCalls) {
|
|
63
|
-
for (const tc of msg.toolCalls) {
|
|
64
|
-
content.push({
|
|
65
|
-
type: "tool_use",
|
|
66
|
-
id: tc.id,
|
|
67
|
-
name: tc.name,
|
|
68
|
-
input: tc.arguments,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (content.length > 0) {
|
|
74
|
-
claudeMessages.push({
|
|
75
|
-
role: "assistant",
|
|
76
|
-
content,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
} else if (msg.role === "tool") {
|
|
80
|
-
claudeMessages.push({
|
|
81
|
-
role: "user",
|
|
82
|
-
content: [
|
|
83
|
-
{
|
|
84
|
-
type: "tool_result",
|
|
85
|
-
tool_use_id: msg.toolCallId!,
|
|
86
|
-
content: msg.content,
|
|
87
|
-
},
|
|
88
|
-
],
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Convert tools to Claude format
|
|
94
|
-
const claudeTools: Anthropic.Tool[] = tools.map((tool) => ({
|
|
95
|
-
name: tool.name,
|
|
96
|
-
description: tool.description,
|
|
97
|
-
input_schema: {
|
|
98
|
-
type: "object" as const,
|
|
99
|
-
properties: tool.parameters.properties as Record<string, unknown>,
|
|
100
|
-
required: tool.parameters.required,
|
|
101
|
-
},
|
|
102
|
-
}));
|
|
103
|
-
|
|
104
|
-
// Make API call
|
|
105
|
-
const response = await this.client.messages.create({
|
|
106
|
-
model: this.model,
|
|
107
|
-
max_tokens: 4096,
|
|
108
|
-
system: systemPrompt,
|
|
109
|
-
messages: claudeMessages,
|
|
110
|
-
tools: claudeTools.length > 0 ? claudeTools : undefined,
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// Parse response
|
|
114
|
-
let textContent = "";
|
|
115
|
-
const toolCalls: ToolCall[] = [];
|
|
116
|
-
|
|
117
|
-
for (const block of response.content) {
|
|
118
|
-
if (block.type === "text") {
|
|
119
|
-
textContent += block.text;
|
|
120
|
-
} else if (block.type === "tool_use") {
|
|
121
|
-
toolCalls.push({
|
|
122
|
-
id: block.id,
|
|
123
|
-
name: block.name,
|
|
124
|
-
arguments: block.input as Record<string, unknown>,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Map stop reason
|
|
130
|
-
let finishReason: LLMResponse["finishReason"] = "stop";
|
|
131
|
-
if (response.stop_reason === "tool_use") {
|
|
132
|
-
finishReason = "tool_calls";
|
|
133
|
-
} else if (response.stop_reason === "max_tokens") {
|
|
134
|
-
finishReason = "length";
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
content: textContent,
|
|
139
|
-
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
140
|
-
finishReason,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
}
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gemini LLM Adapter with Function Calling
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { GoogleGenerativeAI, SchemaType, type Content, type Part, type Tool as GeminiTool, type FunctionDeclarationSchemaProperty } from "@google/generative-ai";
|
|
6
|
-
import type { LLMAdapter } from "./index.js";
|
|
7
|
-
import type {
|
|
8
|
-
ToolDefinition,
|
|
9
|
-
LLMResponse,
|
|
10
|
-
AgentMessage,
|
|
11
|
-
ToolCall,
|
|
12
|
-
LLMProvider,
|
|
13
|
-
} from "../types.js";
|
|
14
|
-
|
|
15
|
-
export class GeminiAdapter implements LLMAdapter {
|
|
16
|
-
readonly provider: LLMProvider = "gemini";
|
|
17
|
-
private client: GoogleGenerativeAI | null = null;
|
|
18
|
-
private model: string = "gemini-2.5-flash";
|
|
19
|
-
|
|
20
|
-
async initialize(apiKey: string): Promise<void> {
|
|
21
|
-
this.client = new GoogleGenerativeAI(apiKey);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
isInitialized(): boolean {
|
|
25
|
-
return this.client !== null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
setModel(model: string): void {
|
|
29
|
-
this.model = model;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async chat(
|
|
33
|
-
messages: AgentMessage[],
|
|
34
|
-
tools: ToolDefinition[]
|
|
35
|
-
): Promise<LLMResponse> {
|
|
36
|
-
if (!this.client) {
|
|
37
|
-
throw new Error("Gemini adapter not initialized");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Extract system message
|
|
41
|
-
const systemMessage = messages.find((m) => m.role === "system");
|
|
42
|
-
const systemInstruction = systemMessage?.content;
|
|
43
|
-
|
|
44
|
-
// Convert tools to Gemini format
|
|
45
|
-
const geminiTools: GeminiTool[] = tools.length > 0 ? [{
|
|
46
|
-
functionDeclarations: tools.map((tool) => ({
|
|
47
|
-
name: tool.name,
|
|
48
|
-
description: tool.description,
|
|
49
|
-
parameters: {
|
|
50
|
-
type: SchemaType.OBJECT,
|
|
51
|
-
properties: tool.parameters.properties as Record<string, FunctionDeclarationSchemaProperty>,
|
|
52
|
-
required: tool.parameters.required,
|
|
53
|
-
},
|
|
54
|
-
})),
|
|
55
|
-
}] : [];
|
|
56
|
-
|
|
57
|
-
// Get model with tools
|
|
58
|
-
const model = this.client.getGenerativeModel({
|
|
59
|
-
model: this.model,
|
|
60
|
-
systemInstruction,
|
|
61
|
-
tools: geminiTools.length > 0 ? geminiTools : undefined,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// Convert messages to Gemini format
|
|
65
|
-
const geminiContents: Content[] = [];
|
|
66
|
-
|
|
67
|
-
for (const msg of messages) {
|
|
68
|
-
if (msg.role === "system") continue;
|
|
69
|
-
|
|
70
|
-
if (msg.role === "user") {
|
|
71
|
-
geminiContents.push({
|
|
72
|
-
role: "user",
|
|
73
|
-
parts: [{ text: msg.content }],
|
|
74
|
-
});
|
|
75
|
-
} else if (msg.role === "assistant") {
|
|
76
|
-
const parts: Part[] = [];
|
|
77
|
-
|
|
78
|
-
if (msg.content) {
|
|
79
|
-
parts.push({ text: msg.content });
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (msg.toolCalls) {
|
|
83
|
-
for (const tc of msg.toolCalls) {
|
|
84
|
-
parts.push({
|
|
85
|
-
functionCall: {
|
|
86
|
-
name: tc.name,
|
|
87
|
-
args: tc.arguments,
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (parts.length > 0) {
|
|
94
|
-
geminiContents.push({
|
|
95
|
-
role: "model",
|
|
96
|
-
parts,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
} else if (msg.role === "tool") {
|
|
100
|
-
// Gemini expects function responses in user turn
|
|
101
|
-
geminiContents.push({
|
|
102
|
-
role: "user",
|
|
103
|
-
parts: [{
|
|
104
|
-
functionResponse: {
|
|
105
|
-
name: msg.toolCallId!.split(":::")[0] || "unknown", // Extract function name from id
|
|
106
|
-
response: { result: msg.content },
|
|
107
|
-
},
|
|
108
|
-
}],
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Make API call
|
|
114
|
-
const result = await model.generateContent({
|
|
115
|
-
contents: geminiContents,
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
const response = result.response;
|
|
119
|
-
const candidate = response.candidates?.[0];
|
|
120
|
-
|
|
121
|
-
if (!candidate) {
|
|
122
|
-
return {
|
|
123
|
-
content: "",
|
|
124
|
-
finishReason: "error",
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Parse response
|
|
129
|
-
let textContent = "";
|
|
130
|
-
const toolCalls: ToolCall[] = [];
|
|
131
|
-
|
|
132
|
-
for (const part of candidate.content.parts) {
|
|
133
|
-
if ("text" in part && part.text) {
|
|
134
|
-
textContent += part.text;
|
|
135
|
-
} else if ("functionCall" in part && part.functionCall) {
|
|
136
|
-
const fc = part.functionCall;
|
|
137
|
-
toolCalls.push({
|
|
138
|
-
id: `${fc.name}:::${Date.now()}`,
|
|
139
|
-
name: fc.name,
|
|
140
|
-
arguments: (fc.args || {}) as Record<string, unknown>,
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Map finish reason
|
|
146
|
-
let finishReason: LLMResponse["finishReason"] = "stop";
|
|
147
|
-
if (toolCalls.length > 0) {
|
|
148
|
-
finishReason = "tool_calls";
|
|
149
|
-
} else if (candidate.finishReason === "MAX_TOKENS") {
|
|
150
|
-
finishReason = "length";
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return {
|
|
154
|
-
content: textContent,
|
|
155
|
-
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
156
|
-
finishReason,
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LLM Adapter Interface and Factory
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { ToolDefinition, LLMResponse, AgentMessage, LLMProvider } from "../types.js";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Abstract interface for LLM providers
|
|
9
|
-
*/
|
|
10
|
-
export interface LLMAdapter {
|
|
11
|
-
/** Provider name */
|
|
12
|
-
readonly provider: LLMProvider;
|
|
13
|
-
|
|
14
|
-
/** Initialize the adapter with API key */
|
|
15
|
-
initialize(apiKey: string): Promise<void>;
|
|
16
|
-
|
|
17
|
-
/** Check if adapter is initialized */
|
|
18
|
-
isInitialized(): boolean;
|
|
19
|
-
|
|
20
|
-
/** Send messages with tools and get response */
|
|
21
|
-
chat(
|
|
22
|
-
messages: AgentMessage[],
|
|
23
|
-
tools: ToolDefinition[]
|
|
24
|
-
): Promise<LLMResponse>;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Factory for creating LLM adapters
|
|
29
|
-
*/
|
|
30
|
-
export async function createAdapter(provider: LLMProvider): Promise<LLMAdapter> {
|
|
31
|
-
switch (provider) {
|
|
32
|
-
case "openai": {
|
|
33
|
-
const { OpenAIAdapter } = await import("./openai.js");
|
|
34
|
-
return new OpenAIAdapter();
|
|
35
|
-
}
|
|
36
|
-
case "claude": {
|
|
37
|
-
const { ClaudeAdapter } = await import("./claude.js");
|
|
38
|
-
return new ClaudeAdapter();
|
|
39
|
-
}
|
|
40
|
-
case "gemini": {
|
|
41
|
-
const { GeminiAdapter } = await import("./gemini.js");
|
|
42
|
-
return new GeminiAdapter();
|
|
43
|
-
}
|
|
44
|
-
case "ollama": {
|
|
45
|
-
const { OllamaAdapter } = await import("./ollama.js");
|
|
46
|
-
return new OllamaAdapter();
|
|
47
|
-
}
|
|
48
|
-
case "xai": {
|
|
49
|
-
const { XAIAdapter } = await import("./xai.js");
|
|
50
|
-
return new XAIAdapter();
|
|
51
|
-
}
|
|
52
|
-
default:
|
|
53
|
-
throw new Error(`Unknown provider: ${provider}`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export { OpenAIAdapter } from "./openai.js";
|
|
58
|
-
export { ClaudeAdapter } from "./claude.js";
|
|
59
|
-
export { GeminiAdapter } from "./gemini.js";
|
|
60
|
-
export { OllamaAdapter } from "./ollama.js";
|
|
61
|
-
export { XAIAdapter } from "./xai.js";
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ollama LLM Adapter with JSON-based tool calling
|
|
3
|
-
* Uses prompt engineering to simulate tool calling for local models
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { LLMAdapter } from "./index.js";
|
|
7
|
-
import type {
|
|
8
|
-
ToolDefinition,
|
|
9
|
-
LLMResponse,
|
|
10
|
-
AgentMessage,
|
|
11
|
-
ToolCall,
|
|
12
|
-
LLMProvider,
|
|
13
|
-
} from "../types.js";
|
|
14
|
-
|
|
15
|
-
interface OllamaResponse {
|
|
16
|
-
model: string;
|
|
17
|
-
created_at: string;
|
|
18
|
-
message: {
|
|
19
|
-
role: string;
|
|
20
|
-
content: string;
|
|
21
|
-
};
|
|
22
|
-
done: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export class OllamaAdapter implements LLMAdapter {
|
|
26
|
-
readonly provider: LLMProvider = "ollama";
|
|
27
|
-
private baseUrl: string = "http://localhost:11434";
|
|
28
|
-
private model: string = "llama3.2";
|
|
29
|
-
private initialized: boolean = false;
|
|
30
|
-
|
|
31
|
-
async initialize(apiKey: string): Promise<void> {
|
|
32
|
-
// apiKey is ignored for Ollama, but we use it to set custom URL if provided
|
|
33
|
-
if (apiKey && apiKey.startsWith("http")) {
|
|
34
|
-
this.baseUrl = apiKey;
|
|
35
|
-
}
|
|
36
|
-
this.initialized = true;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
isInitialized(): boolean {
|
|
40
|
-
return this.initialized;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
setModel(model: string): void {
|
|
44
|
-
this.model = model;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
setBaseUrl(url: string): void {
|
|
48
|
-
this.baseUrl = url;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async chat(
|
|
52
|
-
messages: AgentMessage[],
|
|
53
|
-
tools: ToolDefinition[]
|
|
54
|
-
): Promise<LLMResponse> {
|
|
55
|
-
if (!this.initialized) {
|
|
56
|
-
throw new Error("Ollama adapter not initialized");
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Build system prompt with tool definitions
|
|
60
|
-
const systemMessage = messages.find((m) => m.role === "system");
|
|
61
|
-
let systemPrompt = systemMessage?.content || "";
|
|
62
|
-
|
|
63
|
-
if (tools.length > 0) {
|
|
64
|
-
systemPrompt += "\n\n## IMPORTANT: Tool Usage Instructions\n";
|
|
65
|
-
systemPrompt += "You have access to tools that you MUST use to complete tasks. Do NOT describe how to do something - USE THE TOOLS.\n\n";
|
|
66
|
-
systemPrompt += "To call a tool, respond with ONLY a JSON object in this exact format:\n";
|
|
67
|
-
systemPrompt += '```json\n{"tool_calls": [{"name": "tool_name", "arguments": {"param": "value"}}]}\n```\n\n';
|
|
68
|
-
systemPrompt += "Example - If the user asks to list files:\n";
|
|
69
|
-
systemPrompt += '```json\n{"tool_calls": [{"name": "fs_list", "arguments": {"path": "."}}]}\n```\n\n';
|
|
70
|
-
systemPrompt += "Example - If the user asks to create a project:\n";
|
|
71
|
-
systemPrompt += '```json\n{"tool_calls": [{"name": "project_create", "arguments": {"name": "my-project"}}]}\n```\n\n';
|
|
72
|
-
systemPrompt += "RULES:\n";
|
|
73
|
-
systemPrompt += "1. When asked to DO something, ALWAYS use the appropriate tool\n";
|
|
74
|
-
systemPrompt += "2. Do NOT explain how to use terminal commands - use tools instead\n";
|
|
75
|
-
systemPrompt += "3. After tool results, summarize what was done\n\n";
|
|
76
|
-
systemPrompt += "## Available Tools:\n";
|
|
77
|
-
|
|
78
|
-
for (const tool of tools) {
|
|
79
|
-
systemPrompt += `\n### ${tool.name}\n`;
|
|
80
|
-
systemPrompt += `${tool.description}\n`;
|
|
81
|
-
const params = tool.parameters.properties;
|
|
82
|
-
const required = tool.parameters.required || [];
|
|
83
|
-
if (Object.keys(params).length > 0) {
|
|
84
|
-
systemPrompt += `Parameters:\n`;
|
|
85
|
-
for (const [name, param] of Object.entries(params)) {
|
|
86
|
-
const req = required.includes(name) ? "(required)" : "(optional)";
|
|
87
|
-
systemPrompt += ` - ${name} ${req}: ${(param as { description?: string }).description || ""}\n`;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Convert messages to Ollama format
|
|
94
|
-
const ollamaMessages: { role: string; content: string }[] = [];
|
|
95
|
-
|
|
96
|
-
// Add system message first
|
|
97
|
-
ollamaMessages.push({
|
|
98
|
-
role: "system",
|
|
99
|
-
content: systemPrompt,
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
for (const msg of messages) {
|
|
103
|
-
if (msg.role === "system") continue;
|
|
104
|
-
|
|
105
|
-
if (msg.role === "user") {
|
|
106
|
-
ollamaMessages.push({
|
|
107
|
-
role: "user",
|
|
108
|
-
content: msg.content,
|
|
109
|
-
});
|
|
110
|
-
} else if (msg.role === "assistant") {
|
|
111
|
-
let content = msg.content;
|
|
112
|
-
|
|
113
|
-
// Include tool calls in content for context
|
|
114
|
-
if (msg.toolCalls) {
|
|
115
|
-
content += `\n\nTool calls made:\n${JSON.stringify({ tool_calls: msg.toolCalls }, null, 2)}`;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
ollamaMessages.push({
|
|
119
|
-
role: "assistant",
|
|
120
|
-
content,
|
|
121
|
-
});
|
|
122
|
-
} else if (msg.role === "tool") {
|
|
123
|
-
// Include tool results as user messages for context
|
|
124
|
-
ollamaMessages.push({
|
|
125
|
-
role: "user",
|
|
126
|
-
content: `Tool result (${msg.toolCallId}):\n${msg.content}`,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Make API call
|
|
132
|
-
const response = await fetch(`${this.baseUrl}/api/chat`, {
|
|
133
|
-
method: "POST",
|
|
134
|
-
headers: {
|
|
135
|
-
"Content-Type": "application/json",
|
|
136
|
-
},
|
|
137
|
-
body: JSON.stringify({
|
|
138
|
-
model: this.model,
|
|
139
|
-
messages: ollamaMessages,
|
|
140
|
-
stream: false,
|
|
141
|
-
}),
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
if (!response.ok) {
|
|
145
|
-
throw new Error(`Ollama API error: ${response.status} ${response.statusText}`);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const data = (await response.json()) as OllamaResponse;
|
|
149
|
-
const content = data.message.content;
|
|
150
|
-
|
|
151
|
-
// Try to parse tool calls from response
|
|
152
|
-
const toolCalls = this.parseToolCalls(content);
|
|
153
|
-
|
|
154
|
-
if (toolCalls.length > 0) {
|
|
155
|
-
// Extract text content before the JSON
|
|
156
|
-
const textContent = this.extractTextBeforeJson(content);
|
|
157
|
-
|
|
158
|
-
return {
|
|
159
|
-
content: textContent,
|
|
160
|
-
toolCalls,
|
|
161
|
-
finishReason: "tool_calls",
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
content,
|
|
167
|
-
finishReason: "stop",
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
private parseToolCalls(content: string): ToolCall[] {
|
|
172
|
-
const toolCalls: ToolCall[] = [];
|
|
173
|
-
|
|
174
|
-
// Try to find JSON block with tool_calls
|
|
175
|
-
const jsonMatch = content.match(/```json\s*(\{[\s\S]*?\})\s*```/);
|
|
176
|
-
if (jsonMatch) {
|
|
177
|
-
try {
|
|
178
|
-
const parsed = JSON.parse(jsonMatch[1]);
|
|
179
|
-
if (parsed.tool_calls && Array.isArray(parsed.tool_calls)) {
|
|
180
|
-
for (const tc of parsed.tool_calls) {
|
|
181
|
-
toolCalls.push({
|
|
182
|
-
id: `ollama-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
183
|
-
name: tc.name,
|
|
184
|
-
arguments: tc.arguments || {},
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
} catch {
|
|
189
|
-
// Not valid JSON
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Also try to find raw JSON object
|
|
194
|
-
if (toolCalls.length === 0) {
|
|
195
|
-
const rawJsonMatch = content.match(/\{"tool_calls":\s*\[[\s\S]*?\]\}/);
|
|
196
|
-
if (rawJsonMatch) {
|
|
197
|
-
try {
|
|
198
|
-
const parsed = JSON.parse(rawJsonMatch[0]);
|
|
199
|
-
if (parsed.tool_calls && Array.isArray(parsed.tool_calls)) {
|
|
200
|
-
for (const tc of parsed.tool_calls) {
|
|
201
|
-
toolCalls.push({
|
|
202
|
-
id: `ollama-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
203
|
-
name: tc.name,
|
|
204
|
-
arguments: tc.arguments || {},
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
} catch {
|
|
209
|
-
// Not valid JSON
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return toolCalls;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
private extractTextBeforeJson(content: string): string {
|
|
218
|
-
// Find the start of JSON block
|
|
219
|
-
const jsonStart = content.indexOf("```json");
|
|
220
|
-
if (jsonStart > 0) {
|
|
221
|
-
return content.substring(0, jsonStart).trim();
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const rawJsonStart = content.indexOf('{"tool_calls"');
|
|
225
|
-
if (rawJsonStart > 0) {
|
|
226
|
-
return content.substring(0, rawJsonStart).trim();
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return "";
|
|
230
|
-
}
|
|
231
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenAI LLM Adapter with Function Calling
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import OpenAI from "openai";
|
|
6
|
-
import type { LLMAdapter } from "./index.js";
|
|
7
|
-
import type {
|
|
8
|
-
ToolDefinition,
|
|
9
|
-
LLMResponse,
|
|
10
|
-
AgentMessage,
|
|
11
|
-
ToolCall,
|
|
12
|
-
LLMProvider,
|
|
13
|
-
} from "../types.js";
|
|
14
|
-
|
|
15
|
-
export class OpenAIAdapter implements LLMAdapter {
|
|
16
|
-
readonly provider: LLMProvider = "openai";
|
|
17
|
-
private client: OpenAI | null = null;
|
|
18
|
-
private model: string = "gpt-5-mini";
|
|
19
|
-
|
|
20
|
-
async initialize(apiKey: string): Promise<void> {
|
|
21
|
-
this.client = new OpenAI({ apiKey });
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
isInitialized(): boolean {
|
|
25
|
-
return this.client !== null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
setModel(model: string): void {
|
|
29
|
-
this.model = model;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async chat(
|
|
33
|
-
messages: AgentMessage[],
|
|
34
|
-
tools: ToolDefinition[]
|
|
35
|
-
): Promise<LLMResponse> {
|
|
36
|
-
if (!this.client) {
|
|
37
|
-
throw new Error("OpenAI adapter not initialized");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Convert messages to OpenAI format
|
|
41
|
-
const openaiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = messages.map(
|
|
42
|
-
(msg) => {
|
|
43
|
-
if (msg.role === "tool") {
|
|
44
|
-
return {
|
|
45
|
-
role: "tool" as const,
|
|
46
|
-
tool_call_id: msg.toolCallId!,
|
|
47
|
-
content: msg.content,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
if (msg.role === "assistant" && msg.toolCalls) {
|
|
51
|
-
return {
|
|
52
|
-
role: "assistant" as const,
|
|
53
|
-
content: msg.content || null,
|
|
54
|
-
tool_calls: msg.toolCalls.map((tc) => ({
|
|
55
|
-
id: tc.id,
|
|
56
|
-
type: "function" as const,
|
|
57
|
-
function: {
|
|
58
|
-
name: tc.name,
|
|
59
|
-
arguments: JSON.stringify(tc.arguments),
|
|
60
|
-
},
|
|
61
|
-
})),
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
return {
|
|
65
|
-
role: msg.role as "system" | "user" | "assistant",
|
|
66
|
-
content: msg.content,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
// Convert tools to OpenAI format
|
|
72
|
-
const openaiTools: OpenAI.Chat.ChatCompletionTool[] = tools.map((tool) => ({
|
|
73
|
-
type: "function" as const,
|
|
74
|
-
function: {
|
|
75
|
-
name: tool.name,
|
|
76
|
-
description: tool.description,
|
|
77
|
-
parameters: tool.parameters as unknown as Record<string, unknown>,
|
|
78
|
-
},
|
|
79
|
-
}));
|
|
80
|
-
|
|
81
|
-
// Make API call
|
|
82
|
-
const response = await this.client.chat.completions.create({
|
|
83
|
-
model: this.model,
|
|
84
|
-
messages: openaiMessages,
|
|
85
|
-
tools: openaiTools.length > 0 ? openaiTools : undefined,
|
|
86
|
-
tool_choice: openaiTools.length > 0 ? "auto" : undefined,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const choice = response.choices[0];
|
|
90
|
-
const message = choice.message;
|
|
91
|
-
|
|
92
|
-
// Parse tool calls
|
|
93
|
-
let toolCalls: ToolCall[] | undefined;
|
|
94
|
-
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
95
|
-
toolCalls = message.tool_calls.map((tc) => ({
|
|
96
|
-
id: tc.id,
|
|
97
|
-
name: tc.function.name,
|
|
98
|
-
arguments: JSON.parse(tc.function.arguments),
|
|
99
|
-
}));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Map finish reason
|
|
103
|
-
let finishReason: LLMResponse["finishReason"] = "stop";
|
|
104
|
-
if (choice.finish_reason === "tool_calls") {
|
|
105
|
-
finishReason = "tool_calls";
|
|
106
|
-
} else if (choice.finish_reason === "length") {
|
|
107
|
-
finishReason = "length";
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
content: message.content || "",
|
|
112
|
-
toolCalls,
|
|
113
|
-
finishReason,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
}
|