Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 43 additions & 24 deletions app/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,33 +80,52 @@ async function submit(formData?: FormData, skip?: boolean) {
});
messages.push({ role: 'user', content });

// Call the simplified agent, which now returns data directly.
const analysisResult = await resolutionSearch(messages);
try {
const analysisResult = await resolutionSearch(messages);
const summaryStream = createStreamableValue<string>();
summaryStream.done(analysisResult.summary || 'Analysis complete.');

// Create a streamable value for the summary and mark it as done.
const summaryStream = createStreamableValue<string>();
summaryStream.done(analysisResult.summary || 'Analysis complete.');
uiStream.update(
<BotMessage content={summaryStream.value} />
);

// Update the UI stream with the BotMessage component.
uiStream.update(
<BotMessage content={summaryStream.value} />
);

aiState.done({
...aiState.get(),
messages: [
...aiState.get().messages,
{
id: nanoid(),
role: 'assistant',
content: JSON.stringify(analysisResult),
type: 'resolution_search_result'
}
]
});
aiState.done({
...aiState.get(),
messages: [
...aiState.get().messages,
{
id: nanoid(),
role: 'assistant',
content: JSON.stringify(analysisResult),
type: 'resolution_search_result'
}
]
});
} catch (error) {
console.error('Error during resolution search:', error);
const errorStream = createStreamableValue<string>();
const errorMessage = 'Sorry, I encountered an error while analyzing the image. The AI model might be unavailable or the request timed out. Please try again later.';
errorStream.done(errorMessage);
uiStream.update(
<BotMessage content={errorStream.value} />
);
aiState.done({
...aiState.get(),
messages: [
...aiState.get().messages,
{
id: nanoid(),
role: 'assistant',
content: errorMessage,
type: 'response'
}
Comment on lines +117 to +121

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error-path message uses a different type (response) and unstructured string content, while the success path uses a domain-specific type (resolution_search_result) and structured JSON content. This inconsistency makes downstream handling harder (e.g., consumers may need special-case logic). Prefer a dedicated error type and structured payload for parity and future-proofing.

Suggestion

Consider using a domain-specific error type and a structured JSON payload in the error path to align with the success shape, for example:

const errorPayload = { message: errorMessage };
aiState.done({
  ...aiState.get(),
  messages: [
    ...aiState.get().messages,
    {
      id: nanoid(),
      role: 'assistant',
      content: JSON.stringify(errorPayload),
      type: 'resolution_search_error'
    }
  ]
});

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.

]
});
} finally {
isGenerating.done(false);
uiStream.done();
}

isGenerating.done(false);
uiStream.done();
return {
id: nanoid(),
isGenerating: isGenerating.value,
Expand Down
5 changes: 0 additions & 5 deletions bun.lock
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
"@upstash/redis": "^1.35.0",
"@vercel/analytics": "^1.5.0",
"@vercel/speed-insights": "^1.2.0",
"QCX": ".",
"ai": "^4.3.19",
"build": "^0.1.4",
"class-variance-authority": "^0.7.1",
Expand Down Expand Up @@ -1032,8 +1031,6 @@

"@vercel/speed-insights": ["@vercel/speed-insights@1.2.0", "", { "peerDependencies": { "@sveltejs/kit": "^1 || ^2", "next": ">= 13", "react": "^18 || ^19 || ^19.0.0-rc", "svelte": ">= 4", "vue": "^3", "vue-router": "^4" }, "optionalPeers": ["@sveltejs/kit", "next", "react", "svelte", "vue", "vue-router"] }, "sha512-y9GVzrUJ2xmgtQlzFP2KhVRoCglwfRQgjyfY607aU0hh0Un6d0OUyrJkjuAlsV18qR4zfoFPs/BiIj9YDS6Wzw=="],

"QCX": ["QCX@file:", { "dependencies": { "@ai-sdk/amazon-bedrock": "^1.1.6", "@ai-sdk/anthropic": "^1.2.12", "@ai-sdk/google": "^1.2.22", "@ai-sdk/openai": "^1.3.24", "@ai-sdk/xai": "^1.2.18", "@heroicons/react": "^2.2.0", "@hookform/resolvers": "^5.0.1", "@mapbox/mapbox-gl-draw": "^1.5.0", "@modelcontextprotocol/sdk": "^1.13.0", "@radix-ui/react-alert-dialog": "^1.1.10", "@radix-ui/react-avatar": "^1.1.6", "@radix-ui/react-checkbox": "^1.2.2", "@radix-ui/react-collapsible": "^1.1.7", "@radix-ui/react-dialog": "^1.1.10", "@radix-ui/react-dropdown-menu": "^2.1.11", "@radix-ui/react-label": "^2.1.4", "@radix-ui/react-radio-group": "^1.3.4", "@radix-ui/react-separator": "^1.1.4", "@radix-ui/react-slider": "^1.3.2", "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-switch": "^1.2.2", "@radix-ui/react-tabs": "^1.1.9", "@radix-ui/react-toast": "^1.2.11", "@radix-ui/react-tooltip": "^1.2.3", "@smithery/cli": "^1.2.5", "@smithery/sdk": "^1.0.4", "@supabase/ssr": "^0.3.0", "@supabase/supabase-js": "^2.0.0", "@tailwindcss/typography": "^0.5.16", "@turf/turf": "^7.2.0", "@types/mapbox__mapbox-gl-draw": "^1.4.8", "@types/pg": "^8.15.4", "@upstash/redis": "^1.35.0", "@vercel/analytics": "^1.5.0", "@vercel/speed-insights": "^1.2.0", "QCX": ".", "ai": "^4.3.19", "build": "^0.1.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cookie": "^0.6.0", "dotenv": "^16.5.0", "drizzle-kit": "^0.31.1", "drizzle-orm": "^0.29.0", "embla-carousel-react": "^8.6.0", "exa-js": "^1.6.13", "framer-motion": "^12.15.0", "katex": "^0.16.22", "lottie-react": "^2.4.1", "lucide-react": "^0.507.0", "mapbox-gl": "^3.11.0", "next": "^15.3.3", "next-themes": "^0.3.0", "open-codex": "^0.1.30", "pg": "^8.16.2", "radix-ui": "^1.3.4", "react": "^19.1.0", "react-dom": "^19.1.0", "react-hook-form": "^7.56.2", "react-icons": "^5.5.0", "react-markdown": "^9.1.0", "react-textarea-autosize": "^8.5.9", "react-toastify": "^10.0.6", "rehype-external-links": "^3.0.0", "rehype-katex": "^7.0.1", "remark-gfm": "^4.0.1", "remark-math": "^6.0.0", "smithery": "^0.5.2", "sonner": "^1.7.4", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", "use-mcp": "^0.0.9", "uuid": "^9.0.0", "zod": "^3.23.8" }, "devDependencies": { "@types/cookie": "^0.6.0", "@types/mapbox-gl": "^3.4.1", "@types/node": "^20.17.30", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@types/uuid": "^9.0.0", "cross-env": "^7.0.3", "eslint": "^8.57.1", "eslint-config-next": "^14.2.28", "postcss": "^8.5.3", "tailwindcss": "^3.4.17", "typescript": "^5.8.3" } }],

"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],

"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
Expand Down Expand Up @@ -2568,8 +2565,6 @@

"@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],

"QCX/QCX": ["QCX@file:.", {}],

"ai/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="],

"ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="],
Expand Down