-
-
Notifications
You must be signed in to change notification settings - Fork 0
Ocean/pipeline integration tests d552 #21
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
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 |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import { describe, expect, it } from 'vitest' | ||
| import { parseCompleteSseEvent, serverResponseToChatMessage } from './agentClient' | ||
|
|
||
| describe('parseCompleteSseEvent', () => { | ||
| it('retains provider meta from complete events', () => { | ||
| const raw = JSON.stringify({ | ||
| type: 'complete', | ||
| message: 'Sorted column B', | ||
| actions: [], | ||
| source: 'llm', | ||
| meta: { provider: 'groq', model: 'llama-3.3-70b-versatile' }, | ||
| }) | ||
| const parsed = parseCompleteSseEvent(raw) | ||
| expect(parsed).not.toBeNull() | ||
| expect(parsed!.meta).toEqual({ | ||
| provider: 'groq', | ||
| model: 'llama-3.3-70b-versatile', | ||
| }) | ||
| }) | ||
|
|
||
| it('returns null for token events', () => { | ||
| expect(parseCompleteSseEvent(JSON.stringify({ type: 'token', content: 'hi' }))).toBeNull() | ||
| }) | ||
|
|
||
| it('returns null for malformed JSON', () => { | ||
| expect(parseCompleteSseEvent('{not-json')).toBeNull() | ||
| }) | ||
| }) | ||
|
|
||
| describe('serverResponseToChatMessage', () => { | ||
| it('copies provider meta onto ChatMessage', () => { | ||
| const msg = serverResponseToChatMessage({ | ||
| message: 'Done', | ||
| actions: [], | ||
| source: 'llm', | ||
| meta: { provider: 'groq', model: 'llama-3.3-70b-versatile' }, | ||
| }) | ||
| expect(msg.providerMeta).toEqual({ | ||
| provider: 'groq', | ||
| model: 'llama-3.3-70b-versatile', | ||
| }) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| import { v4 as uuid } from 'uuid' | ||
| import type { AgentAction, ChatMessage } from '@/types' | ||
| import type { AgentAction, ChatMessage, ProviderMeta } from '@/types' | ||
| import type { SpreadsheetContextPayload } from '@/ai/buildContext' | ||
| import { getAuthHeaders } from '@/lib/cloudSync' | ||
| import { getByokPayload } from '@/lib/userApiKey' | ||
|
|
@@ -18,6 +18,33 @@ export interface ServerChatResponse { | |
| source: 'llm' | 'fallback' | 'template' | ||
| reasoning?: string | ||
| suggestions?: string[] | ||
| meta?: ProviderMeta | ||
| } | ||
|
|
||
| /** Parse an SSE `data:` JSON payload into a ServerChatResponse when type=complete. */ | ||
| export function parseCompleteSseEvent(jsonStr: string): ServerChatResponse | null { | ||
| try { | ||
| const event = JSON.parse(jsonStr) as { | ||
| type?: string | ||
| message?: string | ||
| actions?: ServerAgentAction[] | ||
| source?: string | ||
| reasoning?: string | ||
| suggestions?: string[] | ||
| meta?: ProviderMeta | ||
|
Comment on lines
+25
to
+34
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. suggestion (performance): Avoid double JSON.parse for SSE events to reduce overhead and inconsistency risk. Within the stream loop, Suggested implementation: /** Parse a parsed SSE `data:` JSON payload into a ServerChatResponse when type=complete. */
export function parseCompleteSseEvent(event: {
type?: string
message?: string
actions?: ServerAgentAction[]
source?: string
reasoning?: string
suggestions?: string[]
meta?: ProviderMeta
}): ServerChatResponse | null {
try {
if (event.type !== 'complete' || typeof event.message !== 'string') return null
return {To fully avoid double JSON.parse:
|
||
| } | ||
| if (event.type !== 'complete' || typeof event.message !== 'string') return null | ||
| return { | ||
| message: event.message, | ||
| actions: Array.isArray(event.actions) ? event.actions : [], | ||
| source: (event.source as ServerChatResponse['source']) ?? 'llm', | ||
| reasoning: event.reasoning, | ||
| suggestions: event.suggestions, | ||
| meta: event.meta, | ||
|
Comment on lines
+39
to
+43
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. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Validate all optional completion fields before forwarding them.
🤖 Prompt for AI Agents |
||
| } | ||
|
Comment on lines
+36
to
+44
|
||
| } catch { | ||
| return null | ||
| } | ||
| } | ||
|
|
||
| export interface ServerHealth { | ||
|
|
@@ -106,21 +133,13 @@ export async function chatWithAgentServerStream( | |
| if (!jsonStr) continue | ||
|
|
||
| try { | ||
| const event = JSON.parse(jsonStr) as | ||
| | { type: 'token'; content: string } | ||
| | { type: 'complete'; message: string; actions: ServerAgentAction[]; source: string; reasoning?: string; suggestions?: string[] } | ||
|
|
||
| if (event.type === 'token') { | ||
| onToken(event.content) | ||
| } else if (event.type === 'complete') { | ||
| finalResponse = { | ||
| message: event.message, | ||
| actions: event.actions, | ||
| source: event.source as ServerChatResponse['source'], | ||
| reasoning: event.reasoning, | ||
| suggestions: event.suggestions, | ||
| } | ||
| const parsed = JSON.parse(jsonStr) as { type?: string; content?: string } | ||
| if (parsed.type === 'token' && typeof parsed.content === 'string') { | ||
| onToken(parsed.content) | ||
| continue | ||
| } | ||
| const complete = parseCompleteSseEvent(jsonStr) | ||
| if (complete) finalResponse = complete | ||
|
Comment on lines
+136
to
+142
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. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Preserve SSE frames that span stream chunks. A 🤖 Prompt for AI Agents |
||
| } catch { | ||
| // Skip malformed events | ||
| } | ||
|
|
@@ -153,5 +172,6 @@ export function serverResponseToChatMessage( | |
| timestamp, | ||
| suggestions: response.suggestions, | ||
| actions: actions.length > 0 ? actions : undefined, | ||
| providerMeta: response.meta, | ||
| } | ||
| } | ||
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.
suggestion: Defaulting BYOK model to the host name may be misleading or confusing.
Here
providerMeta.modelfalls back tobyokHostwhenbyok.modelis empty, so the "model" field may show a hostname (e.g.api.openai.com) instead of a model identifier. To avoid confusion when debugging or inspecting replies, consider using an explicit placeholder (e.g.'unknown-model'or'custom') or omitting the model rather than reusing the host value.Fix in Cursor