Skip to content

Commit 13c6721

Browse files
committed
fix(providers): retain Prism streaming tool results
1 parent efadbea commit 13c6721

3 files changed

Lines changed: 105 additions & 102 deletions

File tree

apps/sim/providers/prism/index.test.ts

Lines changed: 83 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -129,81 +129,96 @@ describe('prismProvider', () => {
129129
)
130130
})
131131

132-
it('uses the shared tool loop with reasoning replay and returns tool results', async () => {
133-
mockPrepareTools.mockReturnValue({
134-
tools: [
135-
{
136-
type: 'function',
137-
function: {
138-
name: 'lookup',
139-
description: 'Look up a value',
140-
parameters: { type: 'object', properties: {}, required: [] },
141-
},
142-
},
143-
],
144-
toolChoice: 'auto',
145-
forcedTools: [],
146-
})
147-
mockCreateToolStream.mockImplementation(
148-
(options: { onComplete: (result: Record<string, unknown>) => void }) => {
149-
options.onComplete({
150-
content: 'Found it',
151-
tokens: { input: 10, output: 5, total: 15 },
152-
cost: { input: 0, output: 0, toolCost: 0.25, total: 0.25 },
153-
toolCalls: { list: [], count: 0 },
154-
toolResults: [{ value: 'result' }],
155-
modelTime: 1,
156-
toolsTime: 1,
157-
firstResponseTime: 1,
158-
iterations: 2,
159-
})
160-
return new ReadableStream({
161-
start(controller) {
162-
controller.close()
163-
},
164-
})
165-
}
166-
)
167-
168-
const result = await prismProvider.executeRequest(
169-
request({
170-
responseFormat: undefined,
132+
it.each([
133+
{ mode: 'non-streaming', stream: false },
134+
{ mode: 'streaming', stream: true },
135+
])(
136+
'uses the shared tool loop with reasoning replay and returns tool results ($mode)',
137+
async ({ stream }) => {
138+
mockPrepareTools.mockReturnValue({
171139
tools: [
172140
{
173-
id: 'lookup',
174-
description: 'Look up a value',
175-
params: {},
176-
parameters: { type: 'object', properties: {}, required: [] },
141+
type: 'function',
142+
function: {
143+
name: 'lookup',
144+
description: 'Look up a value',
145+
parameters: { type: 'object', properties: {}, required: [] },
146+
},
177147
},
178148
],
149+
toolChoice: 'auto',
150+
forcedTools: [],
179151
})
180-
)
152+
mockCreateToolStream.mockImplementation(
153+
(options: { onComplete: (result: Record<string, unknown>) => void }) => {
154+
options.onComplete({
155+
content: 'Found it',
156+
tokens: { input: 10, output: 5, total: 15 },
157+
cost: { input: 0, output: 0, toolCost: 0.25, total: 0.25 },
158+
toolCalls: { list: [], count: 0 },
159+
toolResults: [{ value: 'result' }],
160+
modelTime: 1,
161+
toolsTime: 1,
162+
firstResponseTime: 1,
163+
iterations: 2,
164+
})
165+
return new ReadableStream({
166+
start(controller) {
167+
controller.close()
168+
},
169+
})
170+
}
171+
)
172+
173+
const result = await prismProvider.executeRequest(
174+
request({
175+
responseFormat: undefined,
176+
stream,
177+
tools: [
178+
{
179+
id: 'lookup',
180+
description: 'Look up a value',
181+
params: {},
182+
parameters: { type: 'object', properties: {}, required: [] },
183+
},
184+
],
185+
})
186+
)
187+
188+
expect(mockCreateToolStream).toHaveBeenCalledWith(
189+
expect.objectContaining({
190+
providerName: 'Prism',
191+
preserveAssistantReasoning: true,
192+
basePayload: expect.objectContaining({
193+
model: 'prism/deepseek-v4.1-flash',
194+
tool_choice: 'auto',
195+
}),
196+
})
197+
)
181198

182-
expect(mockCreateToolStream).toHaveBeenCalledWith(
183-
expect.objectContaining({
184-
providerName: 'Prism',
185-
preserveAssistantReasoning: true,
186-
basePayload: expect.objectContaining({
187-
model: 'prism/deepseek-v4.1-flash',
188-
tool_choice: 'auto',
189-
}),
199+
if ('stream' in result) {
200+
const reader = result.stream.getReader()
201+
while (!(await reader.read()).done) {}
202+
expect(result.execution.output.toolResults).toEqual([{ value: 'result' }])
203+
return
204+
}
205+
206+
expect(result).toMatchObject({ toolResults: [{ value: 'result' }] })
207+
expect(mockCalculateCost).toHaveBeenCalledWith('prism/deepseek-v4.1-flash', 10, 5)
208+
expect(result.cost).toEqual({
209+
input: 0.000003,
210+
output: 0.000006,
211+
toolCost: 0.25,
212+
total: 0.250009,
213+
pricing: {
214+
input: 0.3,
215+
cachedInput: 0.07,
216+
output: 1.2,
217+
updatedAt: '2026-09-12',
218+
},
190219
})
191-
)
192-
expect(result).toMatchObject({ toolResults: [{ value: 'result' }] })
193-
expect(mockCalculateCost).toHaveBeenCalledWith('prism/deepseek-v4.1-flash', 10, 5)
194-
expect(result.cost).toEqual({
195-
input: 0.000003,
196-
output: 0.000006,
197-
toolCost: 0.25,
198-
total: 0.250009,
199-
pricing: {
200-
input: 0.3,
201-
cachedInput: 0.07,
202-
output: 1.2,
203-
updatedAt: '2026-09-12',
204-
},
205-
})
206-
})
220+
}
221+
)
207222

208223
it.each(['none', 'low', 'medium', 'high'])('sends Prism reasoning effort %s', async (value) => {
209224
await prismProvider.executeRequest(request({ reasoningEffort: value }))

apps/sim/providers/prism/index.ts

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import type {
99
import type { NormalizedBlockOutput, StreamingExecution } from '@/executor/types'
1010
import { formatMessagesForProvider } from '@/providers/attachments'
1111
import { getProviderDefaultModel, getProviderModels } from '@/providers/models'
12+
import { createOpenAICompatibleAgentEventStream } from '@/providers/openai-compat/stream-events'
1213
import { createOpenAICompatStreamingToolLoopStream } from '@/providers/openai-compat/streaming-tool-loop'
13-
import { createReadableStreamFromPrismStream } from '@/providers/prism/utils'
1414
import type { AgentStreamEvent } from '@/providers/stream-events'
1515
import { createStreamingExecution } from '@/providers/streaming-execution'
1616
import type { StreamingToolLoopComplete } from '@/providers/streaming-tool-loop-shared'
@@ -183,6 +183,7 @@ export const prismProvider: ProviderConfig = {
183183
output.tokens = result.tokens
184184
output.cost = result.cost
185185
output.toolCalls = result.toolCalls as NormalizedBlockOutput['toolCalls']
186+
output.toolResults = result.toolResults
186187
if (output.providerTiming) {
187188
output.providerTiming.modelTime = result.modelTime
188189
output.providerTiming.toolsTime = result.toolsTime
@@ -251,23 +252,26 @@ export const prismProvider: ProviderConfig = {
251252
isStreaming: true,
252253
streamFormat: 'agent-events-v1',
253254
createStream: ({ output, finalizeTiming }) =>
254-
createReadableStreamFromPrismStream(streamResponse, (content, usage, thinking) => {
255-
output.content = content
256-
output.tokens = {
257-
input: usage.prompt_tokens,
258-
output: usage.completion_tokens,
259-
total: usage.total_tokens,
260-
}
261-
output.cost = calculateCost(
262-
request.model,
263-
usage.prompt_tokens,
264-
usage.completion_tokens
265-
)
266-
if (thinking) {
267-
const segment = output.providerTiming?.timeSegments?.[0]
268-
if (segment) segment.thinkingContent = thinking
269-
}
270-
finalizeTiming()
255+
createOpenAICompatibleAgentEventStream(streamResponse, {
256+
providerName: 'Prism',
257+
onComplete: (result) => {
258+
output.content = result.content
259+
output.tokens = {
260+
input: result.usage.prompt_tokens,
261+
output: result.usage.completion_tokens,
262+
total: result.usage.total_tokens,
263+
}
264+
output.cost = calculateCost(
265+
request.model,
266+
result.usage.prompt_tokens,
267+
result.usage.completion_tokens
268+
)
269+
if (result.thinking) {
270+
const segment = output.providerTiming?.timeSegments?.[0]
271+
if (segment) segment.thinkingContent = result.thinking
272+
}
273+
finalizeTiming()
274+
},
271275
}),
272276
})
273277
}

apps/sim/providers/prism/utils.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

0 commit comments

Comments
 (0)