-
Notifications
You must be signed in to change notification settings - Fork 11
feat: merge features and fixes from prior version of thinkex #59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
dda5f28
a90333b
18259f6
9d1ab1c
0c867f2
4679f8d
1da9224
916ccf8
81380a6
ee44f46
daca01a
ea3d9a4
c3b14d3
97a38ba
ae2b112
e336a72
c1a9ef5
5133435
66aab19
c5983c9
6066574
5002feb
af9aaff
329b863
cd86bcf
9368f9a
31e1248
bf02699
6ece723
c2cda36
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,18 +1,15 @@ | ||||||||||||||||||||||||||||||||||||||
| import { GoogleGenAI } from "@google/genai"; | ||||||||||||||||||||||||||||||||||||||
| import { NextRequest, NextResponse } from "next/server"; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| import { logger } from "@/lib/utils/logger"; | ||||||||||||||||||||||||||||||||||||||
| // Initialize client | ||||||||||||||||||||||||||||||||||||||
| const apiKey = process.env.GOOGLE_GENERATIVE_AI_API_KEY; | ||||||||||||||||||||||||||||||||||||||
| if (!apiKey) { | ||||||||||||||||||||||||||||||||||||||
| console.error("GOOGLE_GENERATIVE_AI_API_KEY is not set"); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const client = new GoogleGenAI({ | ||||||||||||||||||||||||||||||||||||||
| apiKey: apiKey, | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| export const dynamic = 'force-dynamic'; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||
| * GET /api/deep-research/status | ||||||||||||||||||||||||||||||||||||||
| * Poll endpoint to check the status of a deep research interaction | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -21,45 +18,57 @@ export const dynamic = 'force-dynamic'; | |||||||||||||||||||||||||||||||||||||
| export async function GET(req: NextRequest) { | ||||||||||||||||||||||||||||||||||||||
| const searchParams = req.nextUrl.searchParams; | ||||||||||||||||||||||||||||||||||||||
| const interactionId = searchParams.get("interactionId"); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (!interactionId) { | ||||||||||||||||||||||||||||||||||||||
| return NextResponse.json({ error: "interactionId is required" }, { status: 400 }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| // Get the interaction without streaming - this returns the current state | ||||||||||||||||||||||||||||||||||||||
| const interaction = await client.interactions.get(interactionId); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // DEBUG: Log the entire raw interaction object (optional, kept minimal) | ||||||||||||||||||||||||||||||||||||||
| logger.debug('[DEEP-RESEARCH-STATUS] ========== RAW INTERACTION PAYLOAD =========='); | ||||||||||||||||||||||||||||||||||||||
| logger.debug('[DEEP-RESEARCH-STATUS] Interaction ID:', interactionId); | ||||||||||||||||||||||||||||||||||||||
| logger.debug('[DEEP-RESEARCH-STATUS] State object:', (interaction as any).state); | ||||||||||||||||||||||||||||||||||||||
| logger.debug('[DEEP-RESEARCH-STATUS] Status field:', (interaction as any).status); | ||||||||||||||||||||||||||||||||||||||
| // Extract status from interaction - check multiple possible properties | ||||||||||||||||||||||||||||||||||||||
| const rawStatus = (interaction as any).state?.status || | ||||||||||||||||||||||||||||||||||||||
| (interaction as any).status || | ||||||||||||||||||||||||||||||||||||||
| "unknown"; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const rawStatus = (interaction as any).state?.status || | ||||||||||||||||||||||||||||||||||||||
| (interaction as any).status || | ||||||||||||||||||||||||||||||||||||||
| "unknown"; | ||||||||||||||||||||||||||||||||||||||
| // Collect thoughts and report from events or content | ||||||||||||||||||||||||||||||||||||||
| const thoughts: string[] = []; | ||||||||||||||||||||||||||||||||||||||
| let report = ""; | ||||||||||||||||||||||||||||||||||||||
| let error: string | undefined = undefined; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Try to get content directly first (if API returns full content) | ||||||||||||||||||||||||||||||||||||||
| if ((interaction as any).content) { | ||||||||||||||||||||||||||||||||||||||
| // PRIMARY: Extract from outputs array (this is where the API actually returns data) | ||||||||||||||||||||||||||||||||||||||
| const outputs = (interaction as any).outputs || []; | ||||||||||||||||||||||||||||||||||||||
| for (const output of outputs) { | ||||||||||||||||||||||||||||||||||||||
| if (output.type === "text" && output.text) { | ||||||||||||||||||||||||||||||||||||||
| // This is the final report | ||||||||||||||||||||||||||||||||||||||
| report = output.text; | ||||||||||||||||||||||||||||||||||||||
| } else if (output.type === "thought" && output.summary) { | ||||||||||||||||||||||||||||||||||||||
| // This contains the thought summaries | ||||||||||||||||||||||||||||||||||||||
| for (const thought of output.summary) { | ||||||||||||||||||||||||||||||||||||||
| if (thought.text && typeof thought.text === "string" && !thoughts.includes(thought.text)) { | ||||||||||||||||||||||||||||||||||||||
| thoughts.push(thought.text); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+46
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing array check before iterating Line 48 iterates Additionally, Suggested fix } else if (output.type === "thought" && output.summary) {
- // This contains the thought summaries
- for (const thought of output.summary) {
- if (thought.text && typeof thought.text === "string" && !thoughts.includes(thought.text)) {
- thoughts.push(thought.text);
+ // This contains the thought summaries
+ if (Array.isArray(output.summary)) {
+ for (const thought of output.summary) {
+ if (thought.text && typeof thought.text === "string" && !thoughts.includes(thought.text)) {
+ thoughts.push(thought.text);
+ }
}
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| // FALLBACK: Try to get content directly (legacy format) | ||||||||||||||||||||||||||||||||||||||
| if (!report && (interaction as any).content) { | ||||||||||||||||||||||||||||||||||||||
| const content = (interaction as any).content; | ||||||||||||||||||||||||||||||||||||||
| if (typeof content === "string") { | ||||||||||||||||||||||||||||||||||||||
| report = content; | ||||||||||||||||||||||||||||||||||||||
| } else if (Array.isArray(content)) { | ||||||||||||||||||||||||||||||||||||||
| // Content might be an array of content blocks | ||||||||||||||||||||||||||||||||||||||
| for (const block of content) { | ||||||||||||||||||||||||||||||||||||||
| if (block.type === "text" && block.text) { | ||||||||||||||||||||||||||||||||||||||
| report += block.text; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Process events to extract thoughts and report content | ||||||||||||||||||||||||||||||||||||||
| // FALLBACK: Process events to extract thoughts and report content (legacy format) | ||||||||||||||||||||||||||||||||||||||
| const events = (interaction as any).events || []; | ||||||||||||||||||||||||||||||||||||||
| for (const event of events) { | ||||||||||||||||||||||||||||||||||||||
| const eventType = event.event_type || event.type; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (eventType === "content.delta") { | ||||||||||||||||||||||||||||||||||||||
| const delta = event.delta || {}; | ||||||||||||||||||||||||||||||||||||||
| if (delta.type === "text" && delta.text) { | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -72,12 +81,9 @@ export async function GET(req: NextRequest) { | |||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } else if (eventType === "interaction.failed" || eventType === "interaction_failed") { | ||||||||||||||||||||||||||||||||||||||
| error = event.error?.message || event.error || "Research failed"; | ||||||||||||||||||||||||||||||||||||||
| } else if (eventType === "interaction.complete" || eventType === "interaction_complete") { | ||||||||||||||||||||||||||||||||||||||
| // Research completed | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Also check for thoughts in other possible locations | ||||||||||||||||||||||||||||||||||||||
| // FALLBACK: Also check for thoughts in other possible locations | ||||||||||||||||||||||||||||||||||||||
| if ((interaction as any).thoughts && Array.isArray((interaction as any).thoughts)) { | ||||||||||||||||||||||||||||||||||||||
| for (const thought of (interaction as any).thoughts) { | ||||||||||||||||||||||||||||||||||||||
| const thoughtText = typeof thought === "string" ? thought : thought?.text || thought?.content; | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -86,7 +92,6 @@ export async function GET(req: NextRequest) { | |||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Map Google's status to our status format | ||||||||||||||||||||||||||||||||||||||
| let mappedStatus: "researching" | "complete" | "failed" = "researching"; | ||||||||||||||||||||||||||||||||||||||
| const statusLower = String(rawStatus).toLowerCase(); | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -95,7 +100,6 @@ export async function GET(req: NextRequest) { | |||||||||||||||||||||||||||||||||||||
| } else if (statusLower === "failed" || statusLower === "error" || error) { | ||||||||||||||||||||||||||||||||||||||
| mappedStatus = "failed"; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| return NextResponse.json({ | ||||||||||||||||||||||||||||||||||||||
| status: mappedStatus, | ||||||||||||||||||||||||||||||||||||||
| thoughts, | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -105,7 +109,7 @@ export async function GET(req: NextRequest) { | |||||||||||||||||||||||||||||||||||||
| } catch (error: any) { | ||||||||||||||||||||||||||||||||||||||
| console.error("[DEEP_RESEARCH_STATUS] Error:", error); | ||||||||||||||||||||||||||||||||||||||
| return NextResponse.json( | ||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| error: error?.message || "Failed to retrieve interaction status", | ||||||||||||||||||||||||||||||||||||||
| status: "failed" as const, | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Client initialized even when API key is missing.
If
GOOGLE_GENERATIVE_AI_API_KEYis not set, the code logs an error but continues to initialize the client withundefined. This will likely cause cryptic errors later whenclient.interactions.get()is called.Suggested improvement
📝 Committable suggestion
🤖 Prompt for AI Agents