feat: integrate Google Gemini, MiniMax, and GLM providers; fix persistent agent loading toast
Some checks failed
Deploy to Production / Build and Deploy (push) Failing after 1m23s
Some checks failed
Deploy to Production / Build and Deploy (push) Failing after 1m23s
This commit is contained in:
@@ -23,6 +23,9 @@ type AIProvider =
|
|||||||
| 'mistral'
|
| 'mistral'
|
||||||
| 'zai'
|
| 'zai'
|
||||||
| 'lmstudio'
|
| 'lmstudio'
|
||||||
|
| 'google'
|
||||||
|
| 'minimax'
|
||||||
|
| 'glm'
|
||||||
|
|
||||||
/** Providers that cannot be used for embeddings in Memento (no embedding API wired). */
|
/** Providers that cannot be used for embeddings in Memento (no embedding API wired). */
|
||||||
const PROVIDERS_WITHOUT_EMBEDDINGS: AIProvider[] = ['anthropic', 'anthropic_custom']
|
const PROVIDERS_WITHOUT_EMBEDDINGS: AIProvider[] = ['anthropic', 'anthropic_custom']
|
||||||
@@ -45,6 +48,9 @@ const PROVIDER_META: Record<AIProvider, { apiKeyLabel: string; baseUrlLabel: str
|
|||||||
zai: { apiKeyLabel: 'ZAI_API_KEY', baseUrlLabel: '', hasApiKey: true, hasBaseUrl: false, isLocal: false },
|
zai: { apiKeyLabel: 'ZAI_API_KEY', baseUrlLabel: '', hasApiKey: true, hasBaseUrl: false, isLocal: false },
|
||||||
lmstudio: { apiKeyLabel: '', baseUrlLabel: 'admin.ai.baseUrl', hasApiKey: false, hasBaseUrl: true, isLocal: true },
|
lmstudio: { apiKeyLabel: '', baseUrlLabel: 'admin.ai.baseUrl', hasApiKey: false, hasBaseUrl: true, isLocal: true },
|
||||||
custom: { apiKeyLabel: 'CUSTOM_OPENAI_API_KEY', baseUrlLabel: 'admin.ai.baseUrl', hasApiKey: true, hasBaseUrl: true, isLocal: false },
|
custom: { apiKeyLabel: 'CUSTOM_OPENAI_API_KEY', baseUrlLabel: 'admin.ai.baseUrl', hasApiKey: true, hasBaseUrl: true, isLocal: false },
|
||||||
|
google: { apiKeyLabel: 'GOOGLE_GENERATIVE_AI_API_KEY', baseUrlLabel: '', hasApiKey: true, hasBaseUrl: false, isLocal: false },
|
||||||
|
minimax: { apiKeyLabel: 'MINIMAX_API_KEY', baseUrlLabel: '', hasApiKey: true, hasBaseUrl: false, isLocal: false },
|
||||||
|
glm: { apiKeyLabel: 'GLM_API_KEY', baseUrlLabel: '', hasApiKey: true, hasBaseUrl: false, isLocal: false },
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config key names for each provider's API key (used to read/save from config)
|
// Config key names for each provider's API key (used to read/save from config)
|
||||||
@@ -59,6 +65,9 @@ const API_KEY_CONFIG: Record<AIProvider, string> = {
|
|||||||
zai: 'ZAI_API_KEY',
|
zai: 'ZAI_API_KEY',
|
||||||
lmstudio: 'LMSTUDIO_API_KEY',
|
lmstudio: 'LMSTUDIO_API_KEY',
|
||||||
custom: 'CUSTOM_OPENAI_API_KEY',
|
custom: 'CUSTOM_OPENAI_API_KEY',
|
||||||
|
google: 'GOOGLE_GENERATIVE_AI_API_KEY',
|
||||||
|
minimax: 'MINIMAX_API_KEY',
|
||||||
|
glm: 'GLM_API_KEY',
|
||||||
}
|
}
|
||||||
|
|
||||||
const BASE_URL_CONFIG: Record<AIProvider, string> = {
|
const BASE_URL_CONFIG: Record<AIProvider, string> = {
|
||||||
@@ -72,6 +81,9 @@ const BASE_URL_CONFIG: Record<AIProvider, string> = {
|
|||||||
zai: '',
|
zai: '',
|
||||||
lmstudio: 'LMSTUDIO_BASE_URL',
|
lmstudio: 'LMSTUDIO_BASE_URL',
|
||||||
custom: 'CUSTOM_OPENAI_BASE_URL',
|
custom: 'CUSTOM_OPENAI_BASE_URL',
|
||||||
|
google: '',
|
||||||
|
minimax: '',
|
||||||
|
glm: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_BASE_URLS: Record<AIProvider, string> = {
|
const DEFAULT_BASE_URLS: Record<AIProvider, string> = {
|
||||||
@@ -85,6 +97,9 @@ const DEFAULT_BASE_URLS: Record<AIProvider, string> = {
|
|||||||
zai: 'https://api.zukijourney.com/v1',
|
zai: 'https://api.zukijourney.com/v1',
|
||||||
lmstudio: 'http://localhost:1234/v1',
|
lmstudio: 'http://localhost:1234/v1',
|
||||||
custom: '',
|
custom: '',
|
||||||
|
google: '',
|
||||||
|
minimax: 'https://api.minimax.io/v1',
|
||||||
|
glm: 'https://open.bigmodel.ai/api/paas/v4',
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suggested models per provider (shown as hints in Combobox - user can always type a custom name)
|
// Suggested models per provider (shown as hints in Combobox - user can always type a custom name)
|
||||||
@@ -112,6 +127,9 @@ const SUGGESTED_MODELS: Record<string, string[]> = {
|
|||||||
deepseek: ['deepseek-chat', 'deepseek-reasoner'],
|
deepseek: ['deepseek-chat', 'deepseek-reasoner'],
|
||||||
mistral: ['mistral-small-latest', 'mistral-medium-latest', 'mistral-large-latest', 'codestral-latest', 'mistral-embed'],
|
mistral: ['mistral-small-latest', 'mistral-medium-latest', 'mistral-large-latest', 'codestral-latest', 'mistral-embed'],
|
||||||
zai: ['gpt-4.1', 'gpt-4.1-mini', 'gpt-4o', 'gpt-4o-mini', 'claude-sonnet-4', 'gemini-2.5-flash'],
|
zai: ['gpt-4.1', 'gpt-4.1-mini', 'gpt-4o', 'gpt-4o-mini', 'claude-sonnet-4', 'gemini-2.5-flash'],
|
||||||
|
google: ['gemini-2.0-flash', 'gemini-1.5-flash', 'gemini-1.5-pro', 'gemini-1.0-pro'],
|
||||||
|
minimax: ['abab6.5-chat', 'abab6.5s-chat', 'abab5.5-chat'],
|
||||||
|
glm: ['glm-4', 'glm-4-air', 'glm-4-flash', 'glm-3-turbo'],
|
||||||
}
|
}
|
||||||
|
|
||||||
const SUGGESTED_EMBEDDINGS: Record<string, string[]> = {
|
const SUGGESTED_EMBEDDINGS: Record<string, string[]> = {
|
||||||
@@ -605,6 +623,9 @@ export function AdminSettingsForm({ config }: { config: Record<string, string> }
|
|||||||
{ value: 'mistral', label: t('admin.ai.providerMistralOption') },
|
{ value: 'mistral', label: t('admin.ai.providerMistralOption') },
|
||||||
{ value: 'zai', label: t('admin.ai.providerZAIOption') },
|
{ value: 'zai', label: t('admin.ai.providerZAIOption') },
|
||||||
{ value: 'lmstudio', label: t('admin.ai.providerLMStudioOption') },
|
{ value: 'lmstudio', label: t('admin.ai.providerLMStudioOption') },
|
||||||
|
{ value: 'google', label: 'Google Gemini' },
|
||||||
|
{ value: 'minimax', label: 'MiniMax' },
|
||||||
|
{ value: 'glm', label: 'Zhipu GLM' },
|
||||||
{ value: 'custom', label: t('admin.ai.providerCustomOption') },
|
{ value: 'custom', label: t('admin.ai.providerCustomOption') },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -93,24 +93,49 @@ export function AgentCard({ agent, onEdit, onRefresh, onToggle }: AgentCardProps
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (pollRef.current) clearInterval(pollRef.current)
|
if (pollRef.current) clearInterval(pollRef.current)
|
||||||
|
|
||||||
|
const startTime = Date.now()
|
||||||
|
const MAX_POLL_TIME = 10 * 60 * 1000 // 10 minutes
|
||||||
|
|
||||||
pollRef.current = setInterval(async () => {
|
pollRef.current = setInterval(async () => {
|
||||||
|
// Safety timeout
|
||||||
|
if (Date.now() - startTime > MAX_POLL_TIME) {
|
||||||
|
if (pollRef.current) clearInterval(pollRef.current)
|
||||||
|
pollRef.current = null
|
||||||
|
setIsRunning(false)
|
||||||
|
toast.error(t('agents.toasts.runError', { error: 'Timeout after 10 minutes' }), { id: toastId, description: '' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/agents/run-for-note?agentId=${agent.id}`)
|
const res = await fetch(`/api/agents/run-for-note?agentId=${agent.id}`)
|
||||||
|
if (!res.ok) throw new Error('Poll failed')
|
||||||
|
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
if (data.status === 'success') {
|
if (data.status === 'success') {
|
||||||
clearInterval(pollRef.current!)
|
if (pollRef.current) clearInterval(pollRef.current)
|
||||||
pollRef.current = null
|
pollRef.current = null
|
||||||
setIsRunning(false)
|
setIsRunning(false)
|
||||||
toast.success(t('agents.toasts.runSuccess', { name: agent.name }), { id: toastId, duration: 6000 })
|
toast.success(t('agents.toasts.runSuccess', { name: agent.name }), {
|
||||||
|
id: toastId,
|
||||||
|
duration: 6000,
|
||||||
|
description: '' // Clear the loading description
|
||||||
|
})
|
||||||
onRefresh()
|
onRefresh()
|
||||||
} else if (data.status === 'failure') {
|
} else if (data.status === 'failure') {
|
||||||
clearInterval(pollRef.current!)
|
if (pollRef.current) clearInterval(pollRef.current)
|
||||||
pollRef.current = null
|
pollRef.current = null
|
||||||
setIsRunning(false)
|
setIsRunning(false)
|
||||||
toast.error(t('agents.toasts.runError', { error: data.error || t('agents.toasts.runFailed') }), { id: toastId })
|
toast.error(t('agents.toasts.runError', { error: data.error || t('agents.toasts.runFailed') }), {
|
||||||
|
id: toastId,
|
||||||
|
description: '' // Clear the loading description
|
||||||
|
})
|
||||||
onRefresh()
|
onRefresh()
|
||||||
}
|
}
|
||||||
} catch { /* keep polling */ }
|
} catch (err) {
|
||||||
|
console.error('Polling error:', err)
|
||||||
|
// Keep polling until timeout
|
||||||
|
}
|
||||||
}, 3000)
|
}, 3000)
|
||||||
} catch {
|
} catch {
|
||||||
toast.error(t('agents.toasts.runGenericError'), { id: toastId })
|
toast.error(t('agents.toasts.runGenericError'), { id: toastId })
|
||||||
|
|||||||
@@ -2,11 +2,15 @@ import { OpenAIProvider } from './providers/openai';
|
|||||||
import { OllamaProvider } from './providers/ollama';
|
import { OllamaProvider } from './providers/ollama';
|
||||||
import { CustomOpenAIProvider } from './providers/custom-openai';
|
import { CustomOpenAIProvider } from './providers/custom-openai';
|
||||||
import { AnthropicProvider } from './providers/anthropic';
|
import { AnthropicProvider } from './providers/anthropic';
|
||||||
|
import { GoogleProvider } from './providers/google';
|
||||||
import { AIProvider } from './types';
|
import { AIProvider } from './types';
|
||||||
|
|
||||||
type ProviderType =
|
type ProviderType =
|
||||||
| 'ollama'
|
| 'ollama'
|
||||||
| 'openai'
|
| 'openai'
|
||||||
|
| 'google'
|
||||||
|
| 'minimax'
|
||||||
|
| 'glm'
|
||||||
| 'custom'
|
| 'custom'
|
||||||
| 'deepseek'
|
| 'deepseek'
|
||||||
| 'openrouter'
|
| 'openrouter'
|
||||||
@@ -43,6 +47,21 @@ const PROVIDER_DEFAULTS: Record<string, { baseUrl: string; model: string; embedd
|
|||||||
model: '',
|
model: '',
|
||||||
embeddingModel: '',
|
embeddingModel: '',
|
||||||
},
|
},
|
||||||
|
google: {
|
||||||
|
baseUrl: '',
|
||||||
|
model: 'gemini-1.5-flash',
|
||||||
|
embeddingModel: 'text-embedding-004',
|
||||||
|
},
|
||||||
|
minimax: {
|
||||||
|
baseUrl: 'https://api.minimax.io/v1',
|
||||||
|
model: 'abab6.5-chat',
|
||||||
|
embeddingModel: '',
|
||||||
|
},
|
||||||
|
glm: {
|
||||||
|
baseUrl: 'https://open.bigmodel.ai/api/paas/v4',
|
||||||
|
model: 'glm-4',
|
||||||
|
embeddingModel: 'embedding-2',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function createOllamaProvider(config: Record<string, string>, modelName: string, embeddingModelName: string, baseUrlOverride?: string): OllamaProvider {
|
function createOllamaProvider(config: Record<string, string>, modelName: string, embeddingModelName: string, baseUrlOverride?: string): OllamaProvider {
|
||||||
@@ -156,6 +175,26 @@ function createAnthropicCustomProvider(config: Record<string, string>, modelName
|
|||||||
return new AnthropicProvider(apiKey, resolvedModel, baseUrl.trim());
|
return new AnthropicProvider(apiKey, resolvedModel, baseUrl.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createGoogleProvider(config: Record<string, string>, modelName: string, embeddingModelName: string): GoogleProvider {
|
||||||
|
const apiKey = config?.GOOGLE_GENERATIVE_AI_API_KEY || process.env.GOOGLE_GENERATIVE_AI_API_KEY || '';
|
||||||
|
if (!apiKey) throw new Error('GOOGLE_GENERATIVE_AI_API_KEY is required when using Google provider');
|
||||||
|
return new GoogleProvider(apiKey, modelName || PROVIDER_DEFAULTS.google.model, embeddingModelName || PROVIDER_DEFAULTS.google.embeddingModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMiniMaxProvider(config: Record<string, string>, modelName: string, embeddingModelName: string): CustomOpenAIProvider {
|
||||||
|
const apiKey = config?.MINIMAX_API_KEY || process.env.MINIMAX_API_KEY || '';
|
||||||
|
if (!apiKey) throw new Error('MINIMAX_API_KEY is required when using MiniMax provider');
|
||||||
|
const defaults = PROVIDER_DEFAULTS.minimax;
|
||||||
|
return new CustomOpenAIProvider(apiKey, defaults.baseUrl, modelName || defaults.model, embeddingModelName || defaults.embeddingModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createGLMProvider(config: Record<string, string>, modelName: string, embeddingModelName: string): CustomOpenAIProvider {
|
||||||
|
const apiKey = config?.GLM_API_KEY || process.env.GLM_API_KEY || '';
|
||||||
|
if (!apiKey) throw new Error('GLM_API_KEY is required when using GLM provider');
|
||||||
|
const defaults = PROVIDER_DEFAULTS.glm;
|
||||||
|
return new CustomOpenAIProvider(apiKey, defaults.baseUrl, modelName || defaults.model, embeddingModelName || defaults.embeddingModel);
|
||||||
|
}
|
||||||
|
|
||||||
function getProviderInstance(providerType: ProviderType, config: Record<string, string>, modelName: string, embeddingModelName: string, ollamaBaseUrl?: string): AIProvider {
|
function getProviderInstance(providerType: ProviderType, config: Record<string, string>, modelName: string, embeddingModelName: string, ollamaBaseUrl?: string): AIProvider {
|
||||||
switch (providerType) {
|
switch (providerType) {
|
||||||
case 'ollama':
|
case 'ollama':
|
||||||
@@ -178,6 +217,12 @@ function getProviderInstance(providerType: ProviderType, config: Record<string,
|
|||||||
return createAnthropicProvider(config, modelName);
|
return createAnthropicProvider(config, modelName);
|
||||||
case 'anthropic_custom':
|
case 'anthropic_custom':
|
||||||
return createAnthropicCustomProvider(config, modelName);
|
return createAnthropicCustomProvider(config, modelName);
|
||||||
|
case 'google':
|
||||||
|
return createGoogleProvider(config, modelName, embeddingModelName);
|
||||||
|
case 'minimax':
|
||||||
|
return createMiniMaxProvider(config, modelName, embeddingModelName);
|
||||||
|
case 'glm':
|
||||||
|
return createGLMProvider(config, modelName, embeddingModelName);
|
||||||
default:
|
default:
|
||||||
return createOllamaProvider(config, modelName, embeddingModelName, ollamaBaseUrl);
|
return createOllamaProvider(config, modelName, embeddingModelName, ollamaBaseUrl);
|
||||||
}
|
}
|
||||||
@@ -196,6 +241,9 @@ function getProviderConfigKeys(providerType: string): { apiKeyConfigKey: string;
|
|||||||
case 'anthropic': return { apiKeyConfigKey: 'ANTHROPIC_API_KEY', baseUrlConfigKey: '' };
|
case 'anthropic': return { apiKeyConfigKey: 'ANTHROPIC_API_KEY', baseUrlConfigKey: '' };
|
||||||
case 'anthropic_custom':
|
case 'anthropic_custom':
|
||||||
return { apiKeyConfigKey: 'ANTHROPIC_CUSTOM_API_KEY', baseUrlConfigKey: 'ANTHROPIC_CUSTOM_BASE_URL' };
|
return { apiKeyConfigKey: 'ANTHROPIC_CUSTOM_API_KEY', baseUrlConfigKey: 'ANTHROPIC_CUSTOM_BASE_URL' };
|
||||||
|
case 'google': return { apiKeyConfigKey: 'GOOGLE_GENERATIVE_AI_API_KEY', baseUrlConfigKey: '' };
|
||||||
|
case 'minimax': return { apiKeyConfigKey: 'MINIMAX_API_KEY', baseUrlConfigKey: '' };
|
||||||
|
case 'glm': return { apiKeyConfigKey: 'GLM_API_KEY', baseUrlConfigKey: '' };
|
||||||
case 'custom': return { apiKeyConfigKey: 'CUSTOM_OPENAI_API_KEY', baseUrlConfigKey: 'CUSTOM_OPENAI_BASE_URL' };
|
case 'custom': return { apiKeyConfigKey: 'CUSTOM_OPENAI_API_KEY', baseUrlConfigKey: 'CUSTOM_OPENAI_BASE_URL' };
|
||||||
default: return { apiKeyConfigKey: '', baseUrlConfigKey: 'OLLAMA_BASE_URL' };
|
default: return { apiKeyConfigKey: '', baseUrlConfigKey: 'OLLAMA_BASE_URL' };
|
||||||
}
|
}
|
||||||
|
|||||||
129
memento-note/lib/ai/providers/google.ts
Normal file
129
memento-note/lib/ai/providers/google.ts
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
import { createGoogleGenerativeAI } from '@ai-sdk/google';
|
||||||
|
import { generateObject, generateText as aiGenerateText, embed, stepCountIs } from 'ai';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { AIProvider, TagSuggestion, TitleSuggestion, ToolUseOptions, ToolCallResult } from '../types';
|
||||||
|
|
||||||
|
export class GoogleProvider implements AIProvider {
|
||||||
|
private model: any;
|
||||||
|
private embeddingModel: any;
|
||||||
|
|
||||||
|
constructor(apiKey: string, modelName: string = 'gemini-1.5-flash', embeddingModelName: string = 'text-embedding-004') {
|
||||||
|
const google = createGoogleGenerativeAI({
|
||||||
|
apiKey: apiKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.model = google(modelName);
|
||||||
|
this.embeddingModel = google.textEmbeddingModel(embeddingModelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateTags(content: string): Promise<TagSuggestion[]> {
|
||||||
|
try {
|
||||||
|
const { object } = await generateObject({
|
||||||
|
model: this.model,
|
||||||
|
schema: z.object({
|
||||||
|
tags: z.array(z.object({
|
||||||
|
tag: z.string().describe('Short tag name in lowercase'),
|
||||||
|
confidence: z.number().min(0).max(1).describe('Confidence level between 0 and 1')
|
||||||
|
}))
|
||||||
|
}),
|
||||||
|
prompt: `Analyze the following note and suggest 1 to 5 relevant tags.
|
||||||
|
Note content: "${content}"`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return object.tags;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error generating tags (Google):', e);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEmbeddings(text: string): Promise<number[]> {
|
||||||
|
try {
|
||||||
|
const { embedding } = await embed({
|
||||||
|
model: this.embeddingModel,
|
||||||
|
value: text,
|
||||||
|
});
|
||||||
|
return embedding;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error generating embeddings (Google):', e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateTitles(prompt: string): Promise<TitleSuggestion[]> {
|
||||||
|
try {
|
||||||
|
const { object } = await generateObject({
|
||||||
|
model: this.model,
|
||||||
|
schema: z.object({
|
||||||
|
titles: z.array(z.object({
|
||||||
|
title: z.string().describe('Suggested title'),
|
||||||
|
confidence: z.number().min(0).max(1).describe('Confidence level between 0 and 1')
|
||||||
|
}))
|
||||||
|
}),
|
||||||
|
prompt: prompt,
|
||||||
|
});
|
||||||
|
|
||||||
|
return object.titles;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error generating titles (Google):', e);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateText(prompt: string): Promise<string> {
|
||||||
|
try {
|
||||||
|
const { text } = await aiGenerateText({
|
||||||
|
model: this.model,
|
||||||
|
prompt: prompt,
|
||||||
|
});
|
||||||
|
|
||||||
|
return text.trim();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error generating text (Google):', e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async chat(messages: any[], systemPrompt?: string): Promise<any> {
|
||||||
|
try {
|
||||||
|
const { text } = await aiGenerateText({
|
||||||
|
model: this.model,
|
||||||
|
system: systemPrompt,
|
||||||
|
messages: messages,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { text: text.trim() };
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error in chat (Google):', e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateWithTools(options: ToolUseOptions): Promise<ToolCallResult> {
|
||||||
|
const { tools, maxSteps = 10, systemPrompt, messages, prompt } = options
|
||||||
|
const opts: Record<string, any> = {
|
||||||
|
model: this.model,
|
||||||
|
tools,
|
||||||
|
stopWhen: stepCountIs(maxSteps),
|
||||||
|
}
|
||||||
|
if (systemPrompt) opts.system = systemPrompt
|
||||||
|
if (messages) opts.messages = messages
|
||||||
|
else if (prompt) opts.prompt = prompt
|
||||||
|
|
||||||
|
const result = await aiGenerateText(opts as any)
|
||||||
|
return {
|
||||||
|
toolCalls: result.toolCalls?.map((tc: any) => ({ toolName: tc.toolName, input: tc.input })) || [],
|
||||||
|
toolResults: result.toolResults?.map((tr: any) => ({ toolName: tr.toolName, input: tr.input, output: tr.output })) || [],
|
||||||
|
text: result.text,
|
||||||
|
steps: result.steps?.map((step: any) => ({
|
||||||
|
text: step.text,
|
||||||
|
toolCalls: step.toolCalls?.map((tc: any) => ({ toolName: tc.toolName, input: tc.input })) || [],
|
||||||
|
toolResults: step.toolResults?.map((tr: any) => ({ toolName: tr.toolName, input: tr.input, output: tr.output })) || []
|
||||||
|
})) || []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getModel() {
|
||||||
|
return this.model;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,6 +70,9 @@ export interface AIProvider {
|
|||||||
export type AIProviderType =
|
export type AIProviderType =
|
||||||
| 'openai'
|
| 'openai'
|
||||||
| 'ollama'
|
| 'ollama'
|
||||||
|
| 'google'
|
||||||
|
| 'minimax'
|
||||||
|
| 'glm'
|
||||||
| 'custom'
|
| 'custom'
|
||||||
| 'deepseek'
|
| 'deepseek'
|
||||||
| 'openrouter'
|
| 'openrouter'
|
||||||
|
|||||||
46
memento-note/package-lock.json
generated
46
memento-note/package-lock.json
generated
@@ -9,6 +9,7 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/anthropic": "^3.0.76",
|
"@ai-sdk/anthropic": "^3.0.76",
|
||||||
|
"@ai-sdk/google": "^3.0.71",
|
||||||
"@ai-sdk/openai": "^3.0.7",
|
"@ai-sdk/openai": "^3.0.7",
|
||||||
"@ai-sdk/react": "^3.0.170",
|
"@ai-sdk/react": "^3.0.170",
|
||||||
"@auth/prisma-adapter": "^2.11.1",
|
"@auth/prisma-adapter": "^2.11.1",
|
||||||
@@ -175,6 +176,51 @@
|
|||||||
"zod": "^3.25.76 || ^4.1.8"
|
"zod": "^3.25.76 || ^4.1.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@ai-sdk/google": {
|
||||||
|
"version": "3.0.71",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-3.0.71.tgz",
|
||||||
|
"integrity": "sha512-G86UtqkCKM8mQcvsA4FQ1WCRN+w1gl/sxuoYl5CJX5DFSUSkrLNKmLcvi3TtnMvKth5li8W/1h3emQSl2K+qnA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@ai-sdk/provider": "3.0.10",
|
||||||
|
"@ai-sdk/provider-utils": "4.0.27"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.25.76 || ^4.1.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@ai-sdk/google/node_modules/@ai-sdk/provider": {
|
||||||
|
"version": "3.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.10.tgz",
|
||||||
|
"integrity": "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"json-schema": "^0.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@ai-sdk/google/node_modules/@ai-sdk/provider-utils": {
|
||||||
|
"version": "4.0.27",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.27.tgz",
|
||||||
|
"integrity": "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@ai-sdk/provider": "3.0.10",
|
||||||
|
"@standard-schema/spec": "^1.1.0",
|
||||||
|
"eventsource-parser": "^3.0.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"zod": "^3.25.76 || ^4.1.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@ai-sdk/openai": {
|
"node_modules/@ai-sdk/openai": {
|
||||||
"version": "3.0.53",
|
"version": "3.0.53",
|
||||||
"resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-3.0.53.tgz",
|
"resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-3.0.53.tgz",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/anthropic": "^3.0.76",
|
"@ai-sdk/anthropic": "^3.0.76",
|
||||||
|
"@ai-sdk/google": "^3.0.71",
|
||||||
"@ai-sdk/openai": "^3.0.7",
|
"@ai-sdk/openai": "^3.0.7",
|
||||||
"@ai-sdk/react": "^3.0.170",
|
"@ai-sdk/react": "^3.0.170",
|
||||||
"@auth/prisma-adapter": "^2.11.1",
|
"@auth/prisma-adapter": "^2.11.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user