diff --git a/backend/MCP_Execution_Server/src/config.ts b/backend/MCP_Execution_Server/src/config.ts index 9624a1b..c9c3431 100644 --- a/backend/MCP_Execution_Server/src/config.ts +++ b/backend/MCP_Execution_Server/src/config.ts @@ -10,11 +10,11 @@ export const config = { apiKey: process.env.NEYNAR_API_KEY ?? "0x", signerUuid: process.env.NEYNAR_SIGNER_UUID ?? "0x", }, - gemini:{ - apiKey: process.env.GEMINI_API_KEY ?? "0x", + gemini: { + apiKey: process.env.GEMINI_API_KEY ?? "", }, openai: { - apiKey: process.env.OPENAI_API_KEY, + apiKey: process.env.OPENAI_API_KEY ?? "", }, supabase: { SUPABASE_URL: process.env.SUPABASE_URL ?? "", diff --git a/backend/MCP_Execution_Server/src/index.ts b/backend/MCP_Execution_Server/src/index.ts index b8a7d66..2d3d920 100644 --- a/backend/MCP_Execution_Server/src/index.ts +++ b/backend/MCP_Execution_Server/src/index.ts @@ -24,8 +24,8 @@ async function main() { config.reclaim.appId, config.reclaim.appSecret, ); - const geminiai = new GeminiAiService(config.gemini.apiKey as string); - const ai = new AIService(config.openai.apiKey as string); + const geminiai = new GeminiAiService(config.gemini.apiKey); + const ai = new AIService(config.openai.apiKey); const avs = new AVSService( config.network.rpcBaseAddress, config.network.privateKey, @@ -41,7 +41,7 @@ async function main() { avs, ipfs, ); - const user = new UserService(neynar, ai, db); + const user = new UserService(neynar, ai, db, geminiai); // Initialize services const services = { diff --git a/backend/MCP_Execution_Server/src/services/gemini_ai.service.ts b/backend/MCP_Execution_Server/src/services/gemini_ai.service.ts index 0b80a88..007f333 100644 --- a/backend/MCP_Execution_Server/src/services/gemini_ai.service.ts +++ b/backend/MCP_Execution_Server/src/services/gemini_ai.service.ts @@ -1,4 +1,4 @@ -import { GoogleGenAI } from "@google/genai"; +import { GoogleGenAI, Type } from "@google/genai"; export class GeminiAiService { private ai: GoogleGenAI; @@ -9,7 +9,7 @@ export class GeminiAiService { this.ai = new GoogleGenAI({ apiKey: this.geminiApiKey, }); - this.geminiModel = "gemini-2.5-flash"; + this.geminiModel = "gemini-2.5-flash-preview-04-17"; this.embeddingModel = "embedding-001"; } @@ -42,10 +42,17 @@ Bad output: `; try { + /** + * TODO: Fix this + * + * Received error: + * ClientError: got status: 404 Not Found. {"error":{"code":404,"message":"models/gemini-2.5-flash is not found for API version v1beta, or is not supported for generateContent. Call ListModels to see the list of available models and their supported methods.","status":"NOT_FOUND"}} + */ const result = await this.ai.models.generateContent({ model: this.geminiModel, contents: prompt, }); + return result.text; } catch (err: any) { console.error("summarizeUserContext error", err); @@ -151,8 +158,7 @@ ${formattedTrendingFeeds} 7. AVOID talking about airdrops and giveaways. - -You MUST respond with ONLY a valid JSON object containing exactly these two fields: + - replyText: A string with the message "You should connect with [author_username], who said: '[cast_text]'" - Max 60-70 words, 320 characters. - link: A string with the URL "https://warpcast.com/[author_username]/[cast_hash]" - this should MATCH the cast you selected and not a fabricated/random one. @@ -169,30 +175,36 @@ If and only if you find no relevant casts, respond with this exact format: "replyText": "No relevant trending casts found in the provided data.", "link": "" } - - - -Your response should be a valid JSON object with no additional text or explanation. - + `; try { const result = await this.ai.models.generateContent({ model: this.geminiModel, contents: prompt, + config: { + responseMimeType: "application/json", + responseSchema: { + type: Type.OBJECT, + properties: { + replyText: { type: Type.STRING }, + link: { type: Type.STRING }, + }, + required: ["replyText", "link"], + }, + }, }); - const content = result.text?.trim(); - + const content = result.text; try { return content ? JSON.parse(content) : { - replyText: - "No relevant trending casts found in the provided data.", - link: "", - }; + replyText: + "No relevant trending casts found in the provided data.", + link: "", + }; } catch (jsonErr) { - console.warn("Response was not valid JSON:", content); + console.warn("Response was not valid JSON Gemini:", content); return { error: "Invalid JSON format in AI response", raw: content }; } } catch (err: any) { diff --git a/backend/MCP_Execution_Server/src/services/user.service.ts b/backend/MCP_Execution_Server/src/services/user.service.ts index 99224aa..fa5b492 100644 --- a/backend/MCP_Execution_Server/src/services/user.service.ts +++ b/backend/MCP_Execution_Server/src/services/user.service.ts @@ -1,7 +1,7 @@ import type { NeynarService } from "./neynar.service.js"; import type { AIService } from "./ai.service.js"; import type { DBService } from "./db.service.js"; -import { GeminiAiService } from "./gemini_ai.service.js"; +import type { GeminiAiService } from "./gemini_ai.service.js"; export enum FID_STATUS { NOT_EXIST = "NOT_EXIST", @@ -23,8 +23,10 @@ export class UserService { try { if (this.geminiService) { const summary = await this.geminiService.summarizeUserContext(userData); + console.log("Summary generated by Gemini"); if (summary) return summary; } + console.log("Summary generated by AIService"); return await this.aiService.summarizeUserContext(userData); } catch (e) { console.warn("Fallback to AIService for summarizeUserContext"); @@ -32,19 +34,6 @@ export class UserService { } } - private async generateEmbeddingsSafe(text: string): Promise { - try { - if (this.geminiService) { - const embeddings = await this.geminiService.generateEmbeddings(text); - if (embeddings) return embeddings; - } - return await this.aiService.generateEmbeddings(text); - } catch (e) { - console.warn("Fallback to AIService for generateEmbeddings"); - return await this.aiService.generateEmbeddings(text); - } - } - private async generateReplyForCastSafe(input: { userCast: string; castSummary: string; @@ -158,7 +147,7 @@ export class UserService { const userData = await this.neynarService.aggregateUserData(fid); const summary = await this.summarizeUserContextSafe(userData); if (!summary) throw new Error("Summary generation failed"); - const embeddings = await this.generateEmbeddingsSafe(summary); + const embeddings = await this.aiService.generateEmbeddings(summary); if (!embeddings) throw new Error("Embedding generation failed"); const { success } = await this.db.registerAndSubscribeFID( @@ -259,7 +248,8 @@ export class UserService { // Step 2: Generate embeddings for the received cast const castSummary = await this.findMeaningFromTextSafe(cast.text); - const castEmbeddings = await this.generateEmbeddingsSafe(castSummary); + const castEmbeddings = + await this.aiService.generateEmbeddings(castSummary); if (!castEmbeddings) { throw new Error("Embedding generation failed for the cast text"); }