-
Notifications
You must be signed in to change notification settings - Fork 11
Main pr jan20 #86
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
Main pr jan20 #86
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
b597ffc
a7b133d
1c3b14c
4b2f51e
52dbb27
4cb7b7e
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"; | ||
|
Comment on lines
+33
to
+35
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. 🧩 Analysis chain🏁 Script executed: cat -n src/app/api/deep-research/status/route.tsRepository: ThinkEx-OSS/thinkex Length of output: 6444 🌐 Web query:
💡 Result: In Sources: SDK/docs for Interactions & output schema. [1] Google GenAI Interactions docs. [2] Community/SDK reference describing output.reasoning/summary structure. Fix text output accumulation and add defensive type checks for outputs array and summary field. Line 45 uses assignment ( ✅ Safer parsing- const outputs = (interaction as any).outputs || [];
+ const outputs = Array.isArray((interaction as any).outputs)
+ ? (interaction as any).outputs
+ : [];
for (const output of outputs) {
if (output.type === "text" && output.text) {
// This is the final report
- report = output.text;
+ 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);
- }
- }
+ const summaries = Array.isArray(output.summary)
+ ? output.summary
+ : [output.summary];
+ for (const thought of summaries) {
+ const thoughtText =
+ typeof thought === "string" ? thought : thought?.text || thought?.content;
+ if (thoughtText && typeof thoughtText === "string" && !thoughts.includes(thoughtText)) {
+ thoughts.push(thoughtText);
+ }
+ }
}
}🤖 Prompt for AI Agents |
||
| // 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; | ||
|
Contributor
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. P2: The Prompt for AI agents |
||
| } 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); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| // 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, | ||
| }, | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1239,4 +1239,37 @@ body.marquee-selecting iframe { | |||||||||||||||||||||||||||||||||||||||||||
| body:has(.card-detail-modal) .workspace-grid-container { | ||||||||||||||||||||||||||||||||||||||||||||
| opacity: 0; | ||||||||||||||||||||||||||||||||||||||||||||
| pointer-events: none; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| /* Animated gradient border - rotates colors around the border in a circle */ | ||||||||||||||||||||||||||||||||||||||||||||
| @keyframes gradient-rotate { | ||||||||||||||||||||||||||||||||||||||||||||
| 0% { background-position: 0% 0%; } | ||||||||||||||||||||||||||||||||||||||||||||
| 25% { background-position: 100% 0%; } | ||||||||||||||||||||||||||||||||||||||||||||
| 50% { background-position: 100% 100%; } | ||||||||||||||||||||||||||||||||||||||||||||
| 75% { background-position: 0% 100%; } | ||||||||||||||||||||||||||||||||||||||||||||
| 100% { background-position: 0% 0%; } | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| .gradient-border-animated { | ||||||||||||||||||||||||||||||||||||||||||||
| background: linear-gradient( | ||||||||||||||||||||||||||||||||||||||||||||
| 90deg, | ||||||||||||||||||||||||||||||||||||||||||||
| #ef444480, | ||||||||||||||||||||||||||||||||||||||||||||
| #eab30880, | ||||||||||||||||||||||||||||||||||||||||||||
| #22c55e80, | ||||||||||||||||||||||||||||||||||||||||||||
| #3b82f680, | ||||||||||||||||||||||||||||||||||||||||||||
| #a855f780, | ||||||||||||||||||||||||||||||||||||||||||||
| #ef444480 | ||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||
| background-size: 200% 200%; | ||||||||||||||||||||||||||||||||||||||||||||
| animation: gradient-rotate 10s ease-in-out infinite; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1243
to
+1263
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. Remove duplicate gradient border definitions. The 🧹 Suggested fix: remove duplicate definitions-/* Animated gradient border - rotates colors around the border in a circle */
-@keyframes gradient-rotate {
- 0% { background-position: 0% 0%; }
- 25% { background-position: 100% 0%; }
- 50% { background-position: 100% 100%; }
- 75% { background-position: 0% 100%; }
- 100% { background-position: 0% 0%; }
-}
-.gradient-border-animated {
- background: linear-gradient(
- 90deg,
- `#ef444480`,
- `#eab30880`,
- `#22c55e80`,
- `#3b82f680`,
- `#a855f780`,
- `#ef444480`
- );
- background-size: 200% 200%;
- animation: gradient-rotate 10s ease-in-out infinite;
-}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||
| /* Ensure markdown content in cards looks good */ | ||||||||||||||||||||||||||||||||||||||||||||
| .prose { | ||||||||||||||||||||||||||||||||||||||||||||
| max-width: none; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| .prose p { | ||||||||||||||||||||||||||||||||||||||||||||
| margin-top: 0.5em; | ||||||||||||||||||||||||||||||||||||||||||||
| margin-bottom: 0.5em; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
| .prose h1, .prose h2, .prose h3 { | ||||||||||||||||||||||||||||||||||||||||||||
| margin-top: 1em; | ||||||||||||||||||||||||||||||||||||||||||||
| margin-bottom: 0.5em; | ||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||
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.
Guard debug logs to avoid leaking interaction data in production.
The interaction state can contain sensitive content; logging it (and IDs) is risky if debug logging is enabled in prod. Gate these logs or redact fields.
🔒 Suggested guard + redaction
Also applies to: 27-31
🤖 Prompt for AI Agents