mirror of https://github.com/buster-so/buster.git
96 lines
3.7 KiB
TypeScript
96 lines
3.7 KiB
TypeScript
import { createTool } from '@mastra/core/tools';
|
|
import { wrapTraced } from 'braintrust';
|
|
import { z } from 'zod';
|
|
|
|
// Input/Output schemas
|
|
const respondWithoutAssetCreationInputSchema = z.object({
|
|
final_response: z
|
|
.string()
|
|
.min(1, 'Final response is required')
|
|
.describe(
|
|
"The final response message to the user. **MUST** be formatted in Markdown. Use bullet points or other appropriate Markdown formatting. Do not include headers. Do not use the '•' bullet character. Do not include markdown tables."
|
|
),
|
|
});
|
|
|
|
/**
|
|
* Optimistic parsing function for streaming respond-without-asset-creation tool arguments
|
|
* Extracts the final_response field as it's being built incrementally
|
|
*/
|
|
export function parseStreamingArgs(
|
|
accumulatedText: string
|
|
): Partial<z.infer<typeof respondWithoutAssetCreationInputSchema>> | null {
|
|
// Validate input type
|
|
if (typeof accumulatedText !== 'string') {
|
|
throw new Error(`parseStreamingArgs expects string input, got ${typeof accumulatedText}`);
|
|
}
|
|
|
|
try {
|
|
// First try to parse as complete JSON
|
|
const parsed = JSON.parse(accumulatedText);
|
|
return {
|
|
final_response: parsed.final_response !== undefined ? parsed.final_response : undefined,
|
|
};
|
|
} catch (error) {
|
|
// Only catch JSON parse errors - let other errors bubble up
|
|
if (error instanceof SyntaxError) {
|
|
// JSON parsing failed - try regex extraction for partial content
|
|
// Handle both complete and incomplete strings, accounting for escaped quotes
|
|
const match = accumulatedText.match(/"final_response"\s*:\s*"((?:[^"\\]|\\.)*)"/);
|
|
if (match && match[1] !== undefined) {
|
|
// Unescape the string
|
|
const unescaped = match[1].replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
return {
|
|
final_response: unescaped,
|
|
};
|
|
}
|
|
|
|
// Try to extract partial string that's still being built (incomplete quote)
|
|
const partialMatch = accumulatedText.match(/"final_response"\s*:\s*"((?:[^"\\]|\\.)*)/);
|
|
if (partialMatch && partialMatch[1] !== undefined) {
|
|
// Unescape the partial string
|
|
const unescaped = partialMatch[1].replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
return {
|
|
final_response: unescaped,
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
// Unexpected error - re-throw with context
|
|
throw new Error(
|
|
`Unexpected error in parseStreamingArgs: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
);
|
|
}
|
|
}
|
|
|
|
const respondWithoutAssetCreationOutputSchema = z.object({});
|
|
|
|
// Process respond without analysis tool execution
|
|
async function processRespondWithoutAssetCreation(): Promise<
|
|
z.infer<typeof respondWithoutAssetCreationOutputSchema>
|
|
> {
|
|
// This tool signals the end of the workflow and provides the final response.
|
|
// The actual agent termination logic resides elsewhere.
|
|
return {};
|
|
}
|
|
|
|
// Main respond without asset creation function with tracing
|
|
const executeRespondWithoutAssetCreation = wrapTraced(
|
|
async (): Promise<z.infer<typeof respondWithoutAssetCreationOutputSchema>> => {
|
|
return await processRespondWithoutAssetCreation();
|
|
},
|
|
{ name: 'respond-without-asset-creation' }
|
|
);
|
|
|
|
// Export the tool
|
|
export const respondWithoutAssetCreation = createTool({
|
|
id: 'respond-without-asset-creation',
|
|
description:
|
|
"Marks all remaining unfinished tasks as complete, sends a final response to the user, and ends the workflow. Use this when the workflow is finished. This must be in markdown format and not use the '•' bullet character.",
|
|
inputSchema: respondWithoutAssetCreationInputSchema,
|
|
outputSchema: respondWithoutAssetCreationOutputSchema,
|
|
execute: executeRespondWithoutAssetCreation,
|
|
});
|
|
|
|
export default respondWithoutAssetCreation;
|