-
-
Notifications
You must be signed in to change notification settings - Fork 7
Restore Branch History and Integrate Latest Fixes #459
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
base: fix/regressions-security-architecture-13947295106479740171
Are you sure you want to change the base?
Changes from all commits
6eeae28
744e5c9
73924c9
160e082
ae085cf
73c385c
1508c04
c8c028b
e2c0615
d48f378
2c53604
1eb28e1
128e7bd
43179e7
7d45e02
5623831
44e86b6
0a48018
6acfbe5
25a50ed
a674fce
ccd7086
62df7fc
bdd9b5d
2fcf31e
9b98e20
2fef8f0
8ae549a
3495da1
ba36e58
6431b41
8a04d49
10ab3fe
250283e
6554775
e95a25b
f80c87f
c4278e9
813d264
c868dcd
fb33c86
75f7fdf
509e7bf
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 |
|---|---|---|
|
|
@@ -47,6 +47,7 @@ async function submit(formData?: FormData, skip?: boolean) { | |
| const action = formData?.get('action') as string; | ||
| if (action === 'resolution_search') { | ||
| const file = formData?.get('file') as File; | ||
| const timezone = (formData?.get('timezone') as string) || 'UTC'; | ||
| if (!file) { | ||
| throw new Error('No file provided for resolution search.'); | ||
| } | ||
|
|
@@ -60,7 +61,8 @@ async function submit(formData?: FormData, skip?: boolean) { | |
| message.role !== 'tool' && | ||
| message.type !== 'followup' && | ||
| message.type !== 'related' && | ||
| message.type !== 'end' | ||
| message.type !== 'end' && | ||
| message.type !== 'resolution_search_result' | ||
| ); | ||
|
|
||
| // The user's prompt for this action is static. | ||
|
|
@@ -82,74 +84,97 @@ 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) as any; | ||
| // Create a streamable value for the summary. | ||
| const summaryStream = createStreamableValue<string>(''); | ||
|
|
||
| // Create a streamable value for the summary and mark it as done. | ||
| const summaryStream = createStreamableValue<string>(); | ||
| summaryStream.done(analysisResult.summary || 'Analysis complete.'); | ||
| async function processResolutionSearch() { | ||
| try { | ||
| // Call the simplified agent, which now returns a stream result. | ||
| const streamResult = await resolutionSearch(messages, timezone); | ||
|
|
||
| // Update the UI stream with the BotMessage component. | ||
| uiStream.update( | ||
| <BotMessage content={summaryStream.value} /> | ||
| ); | ||
| for await (const partialObject of streamResult.partialObjectStream) { | ||
| if (partialObject.summary) { | ||
| summaryStream.update(partialObject.summary); | ||
| } | ||
| } | ||
|
|
||
| messages.push({ role: 'assistant', content: analysisResult.summary || 'Analysis complete.' }); | ||
| const analysisResult = await streamResult.object; | ||
|
|
||
| const sanitizedMessages: CoreMessage[] = messages.map(m => { | ||
| if (Array.isArray(m.content)) { | ||
| return { | ||
| ...m, | ||
| content: m.content.filter(part => part.type !== 'image') | ||
| } as CoreMessage | ||
| } | ||
| return m | ||
| }) | ||
| // Mark the summary stream as done with the result. | ||
| summaryStream.done(analysisResult.summary || 'Analysis complete.'); | ||
|
|
||
| const relatedQueries = await querySuggestor(uiStream, sanitizedMessages); | ||
| uiStream.append( | ||
| <Section title="Follow-up"> | ||
| messages.push({ role: 'assistant', content: analysisResult.summary || 'Analysis complete.' }); | ||
|
|
||
| const sanitizedMessages: CoreMessage[] = messages.map(m => { | ||
| if (Array.isArray(m.content)) { | ||
| return { | ||
| ...m, | ||
| content: m.content.filter(part => part.type !== 'image') | ||
| } as CoreMessage | ||
| } | ||
| return m | ||
| }) | ||
|
|
||
| const relatedQueries = await querySuggestor(uiStream, sanitizedMessages); | ||
| uiStream.append( | ||
| <Section title="Follow-up"> | ||
| <FollowupPanel /> | ||
| </Section> | ||
| ); | ||
| </Section> | ||
| ); | ||
|
|
||
| await new Promise(resolve => setTimeout(resolve, 500)); | ||
| await new Promise(resolve => setTimeout(resolve, 500)); | ||
|
|
||
| const groupeId = nanoid(); | ||
| const groupeId = nanoid(); | ||
|
|
||
| aiState.done({ | ||
| ...aiState.get(), | ||
| messages: [ | ||
| aiState.done({ | ||
| ...aiState.get(), | ||
| messages: [ | ||
| ...aiState.get().messages, | ||
| { | ||
| id: groupeId, | ||
| role: 'assistant', | ||
| content: analysisResult.summary || 'Analysis complete.', | ||
| type: 'response' | ||
| id: groupeId, | ||
| role: 'assistant', | ||
| content: analysisResult.summary || 'Analysis complete.', | ||
| type: 'response' | ||
| }, | ||
| { | ||
| id: groupeId, | ||
| role: 'assistant', | ||
| content: JSON.stringify(analysisResult), | ||
| type: 'resolution_search_result' | ||
| id: groupeId, | ||
| role: 'assistant', | ||
| content: JSON.stringify(analysisResult), | ||
| type: 'resolution_search_result' | ||
| }, | ||
| { | ||
| id: groupeId, | ||
| role: 'assistant', | ||
| content: JSON.stringify(relatedQueries), | ||
| type: 'related' | ||
| id: groupeId, | ||
| role: 'assistant', | ||
| content: JSON.stringify(relatedQueries), | ||
| type: 'related' | ||
| }, | ||
| { | ||
| id: groupeId, | ||
| role: 'assistant', | ||
| content: 'followup', | ||
| type: 'followup' | ||
| id: groupeId, | ||
| role: 'assistant', | ||
| content: 'followup', | ||
| type: 'followup' | ||
| } | ||
| ] | ||
| }); | ||
| ] | ||
| }); | ||
| } catch (error) { | ||
| console.error('Error in resolution search:', error); | ||
| summaryStream.error(error); | ||
| } finally { | ||
| isGenerating.done(false); | ||
| uiStream.done(); | ||
| } | ||
| } | ||
|
|
||
| // Start the background process without awaiting it. | ||
| processResolutionSearch(); | ||
|
|
||
| // Immediately update the UI stream with the BotMessage component. | ||
| uiStream.update( | ||
| <Section title="response"> | ||
| <BotMessage content={summaryStream.value} /> | ||
| </Section> | ||
| ); | ||
|
Comment on lines
90
to
176
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.
This pattern is only safe if the runtime guarantees the process will continue after the action returns (or if you explicitly use a background/work queue mechanism). SuggestionMake the lifecycle explicit. Options:
uiStream.update(...);
await processResolutionSearch();
Reply with "@CharlieHelps yes please" if you'd like me to add a commit that awaits processing and keeps the streaming UI behavior. |
||
|
|
||
| isGenerating.done(false); | ||
| uiStream.done(); | ||
| return { | ||
| id: nanoid(), | ||
| isGenerating: isGenerating.value, | ||
|
|
@@ -163,7 +188,8 @@ async function submit(formData?: FormData, skip?: boolean) { | |
| message.role !== 'tool' && | ||
| message.type !== 'followup' && | ||
| message.type !== 'related' && | ||
| message.type !== 'end' | ||
| message.type !== 'end' && | ||
| message.type !== 'resolution_search_result' | ||
| ) | ||
|
|
||
| const groupeId = nanoid() | ||
|
|
@@ -295,7 +321,7 @@ async function submit(formData?: FormData, skip?: boolean) { | |
| const hasImage = messageParts.some(part => part.type === 'image') | ||
| // Properly type the content based on whether it contains images | ||
| const content: CoreMessage['content'] = hasImage | ||
| ? messageParts as CoreMessage['content'] | ||
| ? (messageParts as CoreMessage['content']) | ||
| : messageParts.map(part => part.text).join('\n') | ||
|
|
||
| const type = skip | ||
|
|
@@ -340,7 +366,7 @@ async function submit(formData?: FormData, skip?: boolean) { | |
| const mapProvider = formData?.get('mapProvider') as 'mapbox' | 'google' | ||
|
|
||
| async function processEvents() { | ||
| let action: any = { object: { next: 'proceed' } } | ||
| let action: { object: { next: string } } = { object: { next: 'proceed' } } | ||
| if (!skip) { | ||
| const taskManagerResult = await taskManager(messages) | ||
| if (taskManagerResult) { | ||
|
|
@@ -371,7 +397,7 @@ async function submit(formData?: FormData, skip?: boolean) { | |
| let answer = '' | ||
| let toolOutputs: ToolResultPart[] = [] | ||
| let errorOccurred = false | ||
| const streamText = createStreamableValue<string>() | ||
| const streamText = createStreamableValue<string>('') | ||
| uiStream.update(<Spinner />) | ||
|
|
||
| while ( | ||
|
|
@@ -646,7 +672,9 @@ export const getUIStateFromAIState = (aiState: Chat) => { | |
| ) | ||
| } | ||
| case 'related': | ||
| const relatedQueries = createStreamableValue<RelatedQueries>() | ||
| const relatedQueries = createStreamableValue<RelatedQueries>({ | ||
| items: [] | ||
| }) | ||
| relatedQueries.done(JSON.parse(content as string)) | ||
| return { | ||
| id, | ||
|
|
||
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.
summaryStream.error(error)is called with anunknown/non-Errorpossible value. Depending on the stream implementation, this can result in a useless[object Object]message or even a runtime error if it expects anError.Also,
console.erroris fine, but user-visible output should be a safe string/message.Suggestion
Normalize the error before passing it to the stream and provide a user-safe message:
Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this change.