opencode-mem 2.7.7 → 2.8.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/dist/services/ai/ai-provider-factory.d.ts.map +1 -1
- package/dist/services/ai/ai-provider-factory.js +4 -1
- package/dist/services/ai/providers/google-gemini.d.ts +16 -0
- package/dist/services/ai/providers/google-gemini.d.ts.map +1 -0
- package/dist/services/ai/providers/google-gemini.js +226 -0
- package/dist/services/ai/session/session-types.d.ts +1 -1
- package/dist/services/ai/session/session-types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-provider-factory.d.ts","sourceRoot":"","sources":["../../../src/services/ai/ai-provider-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"ai-provider-factory.d.ts","sourceRoot":"","sources":["../../../src/services/ai/ai-provider-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAMnF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,qBAAa,iBAAiB;IAC5B,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,GAAG,cAAc;IAmB3F,MAAM,CAAC,qBAAqB,IAAI,cAAc,EAAE;IAIhD,MAAM,CAAC,sBAAsB,IAAI,MAAM;CAGxC"}
|
|
@@ -2,6 +2,7 @@ import { BaseAIProvider } from "./providers/base-provider.js";
|
|
|
2
2
|
import { OpenAIChatCompletionProvider } from "./providers/openai-chat-completion.js";
|
|
3
3
|
import { OpenAIResponsesProvider } from "./providers/openai-responses.js";
|
|
4
4
|
import { AnthropicMessagesProvider } from "./providers/anthropic-messages.js";
|
|
5
|
+
import { GoogleGeminiProvider } from "./providers/google-gemini.js";
|
|
5
6
|
import { aiSessionManager } from "./session/ai-session-manager.js";
|
|
6
7
|
export class AIProviderFactory {
|
|
7
8
|
static createProvider(providerType, config) {
|
|
@@ -12,12 +13,14 @@ export class AIProviderFactory {
|
|
|
12
13
|
return new OpenAIResponsesProvider(config, aiSessionManager);
|
|
13
14
|
case "anthropic":
|
|
14
15
|
return new AnthropicMessagesProvider(config, aiSessionManager);
|
|
16
|
+
case "google-gemini":
|
|
17
|
+
return new GoogleGeminiProvider(config, aiSessionManager);
|
|
15
18
|
default:
|
|
16
19
|
throw new Error(`Unknown provider type: ${providerType}`);
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
22
|
static getSupportedProviders() {
|
|
20
|
-
return ["openai-chat", "openai-responses", "anthropic"];
|
|
23
|
+
return ["openai-chat", "openai-responses", "anthropic", "google-gemini"];
|
|
21
24
|
}
|
|
22
25
|
static cleanupExpiredSessions() {
|
|
23
26
|
return aiSessionManager.cleanupExpiredSessions();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BaseAIProvider, type ToolCallResult } from "./base-provider.js";
|
|
2
|
+
import { AISessionManager } from "../session/ai-session-manager.js";
|
|
3
|
+
import type { ChatCompletionTool } from "../tools/tool-schema.js";
|
|
4
|
+
/**
|
|
5
|
+
* Google Gemini Provider
|
|
6
|
+
* Supports Google's Gemini models (e.g. gemini-1.5-flash) via Google AI Studio API.
|
|
7
|
+
*/
|
|
8
|
+
export declare class GoogleGeminiProvider extends BaseAIProvider {
|
|
9
|
+
private aiSessionManager;
|
|
10
|
+
constructor(config: any, aiSessionManager: AISessionManager);
|
|
11
|
+
getProviderName(): string;
|
|
12
|
+
supportsSession(): boolean;
|
|
13
|
+
private addToolResponse;
|
|
14
|
+
executeToolCall(systemPrompt: string, userPrompt: string, toolSchema: ChatCompletionTool, sessionId: string): Promise<ToolCallResult>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=google-gemini.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-gemini.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/providers/google-gemini.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAIlE;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,cAAc;IACtD,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,gBAAgB;IAK3D,eAAe,IAAI,MAAM;IAIzB,eAAe,IAAI,OAAO;IAI1B,OAAO,CAAC,eAAe;IA4BjB,eAAe,CACnB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,EAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;CAyN3B"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { BaseAIProvider } from "./base-provider.js";
|
|
2
|
+
import { AISessionManager } from "../session/ai-session-manager.js";
|
|
3
|
+
import { log } from "../../logger.js";
|
|
4
|
+
import { UserProfileValidator } from "../validators/user-profile-validator.js";
|
|
5
|
+
/**
|
|
6
|
+
* Google Gemini Provider
|
|
7
|
+
* Supports Google's Gemini models (e.g. gemini-1.5-flash) via Google AI Studio API.
|
|
8
|
+
*/
|
|
9
|
+
export class GoogleGeminiProvider extends BaseAIProvider {
|
|
10
|
+
aiSessionManager;
|
|
11
|
+
constructor(config, aiSessionManager) {
|
|
12
|
+
super(config);
|
|
13
|
+
this.aiSessionManager = aiSessionManager;
|
|
14
|
+
}
|
|
15
|
+
getProviderName() {
|
|
16
|
+
return "google-gemini";
|
|
17
|
+
}
|
|
18
|
+
supportsSession() {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
addToolResponse(sessionId, messages, toolCallId, content) {
|
|
22
|
+
const sequence = this.aiSessionManager.getLastSequence(sessionId) + 1;
|
|
23
|
+
this.aiSessionManager.addMessage({
|
|
24
|
+
aiSessionId: sessionId,
|
|
25
|
+
sequence,
|
|
26
|
+
role: "tool",
|
|
27
|
+
content,
|
|
28
|
+
toolCallId,
|
|
29
|
+
});
|
|
30
|
+
// Gemini tool response format
|
|
31
|
+
messages.push({
|
|
32
|
+
role: "function",
|
|
33
|
+
parts: [
|
|
34
|
+
{
|
|
35
|
+
functionResponse: {
|
|
36
|
+
name: toolCallId.split(":")[0], // Gemini expects the name of the function
|
|
37
|
+
response: JSON.parse(content),
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async executeToolCall(systemPrompt, userPrompt, toolSchema, sessionId) {
|
|
44
|
+
let session = this.aiSessionManager.getSession(sessionId, "google-gemini");
|
|
45
|
+
if (!session) {
|
|
46
|
+
session = this.aiSessionManager.createSession({
|
|
47
|
+
provider: "google-gemini",
|
|
48
|
+
sessionId,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
const existingMessages = this.aiSessionManager.getMessages(session.id);
|
|
52
|
+
const contents = [];
|
|
53
|
+
// System instruction is separate in Gemini API
|
|
54
|
+
const geminiSystemInstruction = {
|
|
55
|
+
parts: [{ text: systemPrompt }],
|
|
56
|
+
};
|
|
57
|
+
// Convert existing messages to Gemini format
|
|
58
|
+
for (const msg of existingMessages) {
|
|
59
|
+
if (msg.role === "system")
|
|
60
|
+
continue; // Skip system as it's passed separately
|
|
61
|
+
const role = msg.role === "assistant" ? "model" : "user";
|
|
62
|
+
const parts = [];
|
|
63
|
+
if (msg.content) {
|
|
64
|
+
parts.push({ text: msg.content });
|
|
65
|
+
}
|
|
66
|
+
if (msg.toolCalls) {
|
|
67
|
+
for (const tc of msg.toolCalls) {
|
|
68
|
+
parts.push({
|
|
69
|
+
functionCall: {
|
|
70
|
+
name: tc.function.name,
|
|
71
|
+
args: JSON.parse(tc.function.arguments),
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (msg.role === "tool") {
|
|
77
|
+
contents.push({
|
|
78
|
+
role: "function",
|
|
79
|
+
parts: [
|
|
80
|
+
{
|
|
81
|
+
functionResponse: {
|
|
82
|
+
name: (msg.toolCallId || "").split(":")[0],
|
|
83
|
+
response: JSON.parse(msg.content),
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
});
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
contents.push({ role, parts });
|
|
91
|
+
}
|
|
92
|
+
if (contents.length === 0 || contents[contents.length - 1].role !== "user") {
|
|
93
|
+
const userSequence = this.aiSessionManager.getLastSequence(session.id) + 1;
|
|
94
|
+
this.aiSessionManager.addMessage({
|
|
95
|
+
aiSessionId: session.id,
|
|
96
|
+
sequence: userSequence,
|
|
97
|
+
role: "user",
|
|
98
|
+
content: userPrompt,
|
|
99
|
+
});
|
|
100
|
+
contents.push({ role: "user", parts: [{ text: userPrompt }] });
|
|
101
|
+
}
|
|
102
|
+
let iterations = 0;
|
|
103
|
+
const maxIterations = this.config.maxIterations ?? 5;
|
|
104
|
+
const iterationTimeout = this.config.iterationTimeout ?? 30000;
|
|
105
|
+
// Gemini API expects the tool name as a function declaration
|
|
106
|
+
const tools = [
|
|
107
|
+
{
|
|
108
|
+
functionDeclarations: [
|
|
109
|
+
{
|
|
110
|
+
name: toolSchema.function.name,
|
|
111
|
+
description: toolSchema.function.description,
|
|
112
|
+
parameters: toolSchema.function.parameters,
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
while (iterations < maxIterations) {
|
|
118
|
+
iterations++;
|
|
119
|
+
const controller = new AbortController();
|
|
120
|
+
const timeout = setTimeout(() => controller.abort(), iterationTimeout);
|
|
121
|
+
try {
|
|
122
|
+
const baseUrl = this.config.apiUrl || "https://generativelanguage.googleapis.com/v1beta";
|
|
123
|
+
const url = `${baseUrl}/models/${this.config.model}:generateContent?key=${this.config.apiKey}`;
|
|
124
|
+
const requestBody = {
|
|
125
|
+
contents,
|
|
126
|
+
systemInstruction: geminiSystemInstruction,
|
|
127
|
+
tools,
|
|
128
|
+
toolConfig: {
|
|
129
|
+
functionCallingConfig: {
|
|
130
|
+
mode: "ANY", // Force function calling
|
|
131
|
+
allowedFunctionNames: [toolSchema.function.name],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
generationConfig: {
|
|
135
|
+
temperature: this.config.memoryTemperature ?? 0.3,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
const response = await fetch(url, {
|
|
139
|
+
method: "POST",
|
|
140
|
+
headers: { "Content-Type": "application/json" },
|
|
141
|
+
body: JSON.stringify(requestBody),
|
|
142
|
+
signal: controller.signal,
|
|
143
|
+
});
|
|
144
|
+
clearTimeout(timeout);
|
|
145
|
+
if (!response.ok) {
|
|
146
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
147
|
+
log("Gemini API error", {
|
|
148
|
+
status: response.status,
|
|
149
|
+
error: errorText,
|
|
150
|
+
iteration: iterations,
|
|
151
|
+
});
|
|
152
|
+
return {
|
|
153
|
+
success: false,
|
|
154
|
+
error: `Gemini API error: ${response.status} - ${errorText}`,
|
|
155
|
+
iterations,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const data = (await response.json());
|
|
159
|
+
const candidate = data.candidates?.[0];
|
|
160
|
+
if (!candidate || !candidate.content) {
|
|
161
|
+
return { success: false, error: "Invalid Gemini API response format", iterations };
|
|
162
|
+
}
|
|
163
|
+
const modelMsg = candidate.content;
|
|
164
|
+
const assistantSequence = this.aiSessionManager.getLastSequence(session.id) + 1;
|
|
165
|
+
// Map Gemini response back to our internal message format
|
|
166
|
+
const assistantMsg = {
|
|
167
|
+
aiSessionId: session.id,
|
|
168
|
+
sequence: assistantSequence,
|
|
169
|
+
role: "assistant",
|
|
170
|
+
content: "",
|
|
171
|
+
toolCalls: [],
|
|
172
|
+
};
|
|
173
|
+
for (const part of modelMsg.parts) {
|
|
174
|
+
if (part.text)
|
|
175
|
+
assistantMsg.content += part.text;
|
|
176
|
+
if (part.functionCall) {
|
|
177
|
+
assistantMsg.toolCalls.push({
|
|
178
|
+
id: `${part.functionCall.name}:${Date.now()}`,
|
|
179
|
+
type: "function",
|
|
180
|
+
function: {
|
|
181
|
+
name: part.functionCall.name,
|
|
182
|
+
arguments: JSON.stringify(part.functionCall.args),
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
this.aiSessionManager.addMessage(assistantMsg);
|
|
188
|
+
contents.push(modelMsg);
|
|
189
|
+
if (assistantMsg.toolCalls.length > 0) {
|
|
190
|
+
for (const toolCall of assistantMsg.toolCalls) {
|
|
191
|
+
if (toolCall.function.name === toolSchema.function.name) {
|
|
192
|
+
try {
|
|
193
|
+
const parsed = JSON.parse(toolCall.function.arguments);
|
|
194
|
+
const result = UserProfileValidator.validate(parsed);
|
|
195
|
+
if (!result.valid)
|
|
196
|
+
throw new Error(result.errors.join(", "));
|
|
197
|
+
this.addToolResponse(session.id, contents, toolCall.id, JSON.stringify({ success: true }));
|
|
198
|
+
return { success: true, data: result.data, iterations };
|
|
199
|
+
}
|
|
200
|
+
catch (validationError) {
|
|
201
|
+
const errorMessage = `Validation failed: ${String(validationError)}`;
|
|
202
|
+
this.addToolResponse(session.id, contents, toolCall.id, JSON.stringify({ success: false, error: errorMessage }));
|
|
203
|
+
return { success: false, error: errorMessage, iterations };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Retry if no tool call was made
|
|
209
|
+
const retryPrompt = "Please use the save_memories tool as instructed.";
|
|
210
|
+
const retrySequence = this.aiSessionManager.getLastSequence(session.id) + 1;
|
|
211
|
+
this.aiSessionManager.addMessage({
|
|
212
|
+
aiSessionId: session.id,
|
|
213
|
+
sequence: retrySequence,
|
|
214
|
+
role: "user",
|
|
215
|
+
content: retryPrompt,
|
|
216
|
+
});
|
|
217
|
+
contents.push({ role: "user", parts: [{ text: retryPrompt }] });
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
clearTimeout(timeout);
|
|
221
|
+
return { success: false, error: String(error), iterations };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return { success: false, error: `Max iterations (${maxIterations}) reached`, iterations };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-types.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/session/session-types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,kBAAkB,GAAG,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"session-types.d.ts","sourceRoot":"","sources":["../../../../src/services/ai/session/session-types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,kBAAkB,GAAG,WAAW,GAAG,eAAe,CAAC;AAEhG,MAAM,WAAW,SAAS;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,UAAU,CAAC;QACjB,QAAQ,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,CAAC;KAC/C,CAAC,CAAC;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,KAAK,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC,CAAC;IACH,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,cAAc,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,cAAc,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC"}
|