This commit is contained in:
Siddharth Ganesan
2025-07-08 19:46:13 -07:00
parent ccf5c2f6d8
commit 776ae06671
7 changed files with 72 additions and 46 deletions

View File

@@ -77,20 +77,28 @@ export async function POST(req: NextRequest) {
// Handle streaming response (ReadableStream or StreamingExecution)
let streamToRead: ReadableStream | null = null
// Debug logging to see what we actually got
logger.info(`[${requestId}] Response type analysis:`, {
responseType: typeof result.response,
isReadableStream: result.response instanceof ReadableStream,
hasStreamProperty: typeof result.response === 'object' && result.response && 'stream' in result.response,
hasExecutionProperty: typeof result.response === 'object' && result.response && 'execution' in result.response,
responseKeys: typeof result.response === 'object' && result.response ? Object.keys(result.response) : [],
hasStreamProperty:
typeof result.response === 'object' && result.response && 'stream' in result.response,
hasExecutionProperty:
typeof result.response === 'object' && result.response && 'execution' in result.response,
responseKeys:
typeof result.response === 'object' && result.response ? Object.keys(result.response) : [],
})
if (result.response instanceof ReadableStream) {
logger.info(`[${requestId}] Direct ReadableStream detected`)
streamToRead = result.response
} else if (typeof result.response === 'object' && result.response && 'stream' in result.response && 'execution' in result.response) {
} else if (
typeof result.response === 'object' &&
result.response &&
'stream' in result.response &&
'execution' in result.response
) {
// Handle StreamingExecution (from providers with tool calls)
logger.info(`[${requestId}] StreamingExecution detected`)
streamToRead = (result.response as any).stream

View File

@@ -48,10 +48,10 @@ export async function POST(req: NextRequest) {
logger.error(`[${requestId}] Documentation search error:`, error)
return NextResponse.json(
{
{
error: 'Failed to search documentation',
details: error instanceof Error ? error.message : 'Unknown error'
},
details: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
)
}

View File

@@ -251,10 +251,10 @@ export async function sendStreamingMessage(request: SendMessageRequest): Promise
error?: string
}> {
try {
console.log('[CopilotAPI] Sending streaming message request:', {
message: request.message,
console.log('[CopilotAPI] Sending streaming message request:', {
message: request.message,
stream: true,
hasWorkflowId: !!request.workflowId
hasWorkflowId: !!request.workflowId,
})
const response = await fetch('/api/copilot', {
@@ -268,7 +268,7 @@ export async function sendStreamingMessage(request: SendMessageRequest): Promise
status: response.status,
statusText: response.statusText,
hasBody: !!response.body,
contentType: response.headers.get('content-type')
contentType: response.headers.get('content-type'),
})
if (!response.ok) {
@@ -351,19 +351,19 @@ export async function sendStreamingDocsMessage(request: DocsQueryRequest): Promi
}> {
try {
console.log('[CopilotAPI] sendStreamingDocsMessage called with:', request)
const response = await fetch('/api/copilot/docs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...request, stream: true }),
})
console.log('[CopilotAPI] Fetch response received:', {
status: response.status,
statusText: response.statusText,
console.log('[CopilotAPI] Fetch response received:', {
status: response.status,
statusText: response.statusText,
headers: Object.fromEntries(response.headers.entries()),
ok: response.ok,
hasBody: !!response.body
hasBody: !!response.body,
})
if (!response.ok) {

View File

@@ -445,7 +445,12 @@ export async function generateChatResponse(
})
// Handle StreamingExecution (from providers with tool calls)
if (typeof response === 'object' && response && 'stream' in response && 'execution' in response) {
if (
typeof response === 'object' &&
response &&
'stream' in response &&
'execution' in response
) {
logger.info('Detected StreamingExecution from provider')
return (response as any).stream
}
@@ -453,7 +458,7 @@ export async function generateChatResponse(
// Handle ProviderResponse (non-streaming with tool calls)
if (typeof response === 'object' && 'content' in response) {
const content = response.content || 'Sorry, I could not generate a response.'
// If streaming was requested, wrap the content in a ReadableStream
if (stream) {
return new ReadableStream({
@@ -461,10 +466,10 @@ export async function generateChatResponse(
const encoder = new TextEncoder()
controller.enqueue(encoder.encode(content))
controller.close()
}
},
})
}
return content
}

View File

@@ -27,7 +27,8 @@ export interface CopilotTool {
const docsSearchTool: CopilotTool = {
id: 'docs_search_internal',
name: 'Search Documentation',
description: 'Search Sim Studio documentation for information about features, tools, workflows, and functionality',
description:
'Search Sim Studio documentation for information about features, tools, workflows, and functionality',
parameters: {
type: 'object',
properties: {
@@ -46,13 +47,13 @@ const docsSearchTool: CopilotTool = {
execute: async (args: Record<string, any>): Promise<CopilotToolResult> => {
try {
const { query, topK = 5 } = args
logger.info('Executing documentation search', { query, topK })
const results = await searchDocumentation(query, { topK })
logger.info(`Found ${results.length} documentation results`, { query })
return {
success: true,
data: {
@@ -87,7 +88,7 @@ export async function executeCopilotTool(
args: Record<string, any>
): Promise<CopilotToolResult> {
const tool = getCopilotTool(toolId)
if (!tool) {
logger.error(`Copilot tool not found: ${toolId}`)
return {
@@ -113,4 +114,4 @@ export async function executeCopilotTool(
// Get all available copilot tools (for tool definitions in LLM requests)
export function getAllCopilotTools(): CopilotTool[] {
return Object.values(copilotTools)
}
}

View File

@@ -196,11 +196,11 @@ export const useCopilotStore = create<CopilotStore>()(
const { workflowId, currentChat } = get()
const { stream = true } = options
console.log('[CopilotStore] sendMessage called:', {
message,
workflowId,
hasCurrentChat: !!currentChat,
stream
console.log('[CopilotStore] sendMessage called:', {
message,
workflowId,
hasCurrentChat: !!currentChat,
stream,
})
if (!workflowId) {
@@ -227,9 +227,9 @@ export const useCopilotStore = create<CopilotStore>()(
timestamp: new Date().toISOString(),
}
console.log('[CopilotStore] Adding messages to state:', {
userMessageId: userMessage.id,
streamingMessageId: streamingMessage.id
console.log('[CopilotStore] Adding messages to state:', {
userMessageId: userMessage.id,
streamingMessageId: streamingMessage.id,
})
set((state) => ({
@@ -246,10 +246,10 @@ export const useCopilotStore = create<CopilotStore>()(
stream,
})
console.log('[CopilotStore] Streaming result:', {
success: result.success,
hasStream: !!result.stream,
error: result.error
console.log('[CopilotStore] Streaming result:', {
success: result.success,
hasStream: !!result.stream,
error: result.error,
})
if (result.success && result.stream) {
@@ -353,7 +353,10 @@ export const useCopilotStore = create<CopilotStore>()(
// Handle streaming response (shared by both message types)
handleStreamingResponse: async (stream: ReadableStream, messageId: string) => {
console.log('[CopilotStore] handleStreamingResponse started:', { messageId, hasStream: !!stream })
console.log('[CopilotStore] handleStreamingResponse started:', {
messageId,
hasStream: !!stream,
})
const reader = stream.getReader()
const decoder = new TextDecoder()
@@ -365,7 +368,7 @@ export const useCopilotStore = create<CopilotStore>()(
try {
while (true) {
const { done, value } = await reader.read()
if (done || streamComplete) break
const chunk = decoder.decode(value, { stream: true })
@@ -395,7 +398,10 @@ export const useCopilotStore = create<CopilotStore>()(
} else if (data.type === 'content') {
console.log('[CopilotStore] Received content chunk:', data.content)
accumulatedContent += data.content
console.log('[CopilotStore] Accumulated content length:', accumulatedContent.length)
console.log(
'[CopilotStore] Accumulated content length:',
accumulatedContent.length
)
// Update the streaming message
set((state) => ({
@@ -443,7 +449,12 @@ export const useCopilotStore = create<CopilotStore>()(
throw new Error(data.error || 'Streaming error')
}
} catch (parseError) {
console.warn('[CopilotStore] Failed to parse SSE data:', parseError, 'Line:', line)
console.warn(
'[CopilotStore] Failed to parse SSE data:',
parseError,
'Line:',
line
)
logger.warn('Failed to parse SSE data:', parseError)
}
} else if (line.trim()) {

View File

@@ -20,7 +20,8 @@ export interface DocsSearchResponse {
export const docsSearchTool: ToolConfig<DocsSearchParams, DocsSearchResponse> = {
id: 'docs_search_internal',
name: 'Search Documentation',
description: 'Search Sim Studio documentation for information about features, tools, workflows, and functionality',
description:
'Search Sim Studio documentation for information about features, tools, workflows, and functionality',
version: '1.0.0',
params: {