|
|
|
|
@@ -192,7 +192,43 @@ const CHAT_TOOLS: any[] = [
|
|
|
|
|
...(env.CHAT_SHELL_TOOL_ENABLED ? [SHELL_EXEC_TOOL] : []),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const RESPONSES_CHAT_TOOLS: any[] = CHAT_TOOLS.map((tool) => {
|
|
|
|
|
function getToolName(tool: any) {
|
|
|
|
|
return typeof tool?.function?.name === "string" ? tool.function.name : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getAvailableChatTools() {
|
|
|
|
|
return CHAT_TOOLS.map((tool) => {
|
|
|
|
|
const name = getToolName(tool);
|
|
|
|
|
if (!name) return null;
|
|
|
|
|
return {
|
|
|
|
|
name,
|
|
|
|
|
description: typeof tool?.function?.description === "string" ? tool.function.description : "",
|
|
|
|
|
};
|
|
|
|
|
}).filter((tool): tool is { name: string; description: string } => tool !== null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function normalizeEnabledChatTools(value: unknown) {
|
|
|
|
|
if (!Array.isArray(value)) return getAvailableChatTools().map((tool) => tool.name);
|
|
|
|
|
const available = new Set(getAvailableChatTools().map((tool) => tool.name));
|
|
|
|
|
return [...new Set(value.filter((item): item is string => typeof item === "string").map((item) => item.trim()).filter(Boolean))].filter((name) =>
|
|
|
|
|
available.has(name)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getEnabledToolSet(params: Pick<ToolAwareCompletionParams, "enabledTools">) {
|
|
|
|
|
return new Set(normalizeEnabledChatTools(params.enabledTools));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getEnabledChatTools(params: Pick<ToolAwareCompletionParams, "enabledTools">) {
|
|
|
|
|
const enabled = getEnabledToolSet(params);
|
|
|
|
|
return CHAT_TOOLS.filter((tool) => {
|
|
|
|
|
const name = getToolName(tool);
|
|
|
|
|
return name ? enabled.has(name) : false;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toResponsesChatTools(tools: any[]) {
|
|
|
|
|
return tools.map((tool) => {
|
|
|
|
|
if (tool?.type !== "function") return tool;
|
|
|
|
|
return {
|
|
|
|
|
type: "function",
|
|
|
|
|
@@ -201,7 +237,8 @@ const RESPONSES_CHAT_TOOLS: any[] = CHAT_TOOLS.map((tool) => {
|
|
|
|
|
parameters: tool.function.parameters,
|
|
|
|
|
strict: false,
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const CHAT_TOOL_SYSTEM_PROMPT =
|
|
|
|
|
"You can use tools to gather up-to-date web information when needed. " +
|
|
|
|
|
@@ -243,6 +280,7 @@ type ToolAwareCompletionParams = {
|
|
|
|
|
client: OpenAI;
|
|
|
|
|
model: string;
|
|
|
|
|
messages: ChatMessage[];
|
|
|
|
|
enabledTools?: string[];
|
|
|
|
|
userLocation?: string;
|
|
|
|
|
temperature?: number;
|
|
|
|
|
maxTokens?: number;
|
|
|
|
|
@@ -384,20 +422,38 @@ function extractHtmlTitle(html: string) {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeIncomingMessages(messages: ChatMessage[], userLocation?: string) {
|
|
|
|
|
function buildChatToolSystemPrompt(params: Pick<ToolAwareCompletionParams, "enabledTools">) {
|
|
|
|
|
const enabled = getEnabledToolSet(params);
|
|
|
|
|
return (
|
|
|
|
|
"You can use tools to gather up-to-date web information when needed. " +
|
|
|
|
|
(enabled.has("web_search") ? "Use web_search for discovery and recent facts. " : "") +
|
|
|
|
|
(enabled.has("fetch_url") ? "Use fetch_url to read the full content of a specific page. " : "") +
|
|
|
|
|
"Prefer tools when the user asks for current events, verification, sources, or details you do not already have. " +
|
|
|
|
|
"When you decide tool use is needed, call the tool immediately in the same response; do not say you are running a tool unless you actually call it. " +
|
|
|
|
|
(enabled.has("codex_exec")
|
|
|
|
|
? "Use codex_exec when a request needs substantial coding work, repository inspection, shell commands, tests, debugging, or another complex task suited to a persistent Codex workspace. Provide codex_exec a complete prompt with the goal, constraints, assumptions, and expected report-back format. Never ask codex_exec to wait for user input or run interactive commands. "
|
|
|
|
|
: "") +
|
|
|
|
|
(enabled.has("shell_exec")
|
|
|
|
|
? "Use shell_exec for direct non-interactive command-line work on the remote devbox, including quick Python programs, calculations, file inspection, running tests, and small scripts. "
|
|
|
|
|
: "") +
|
|
|
|
|
"Do not fabricate tool outputs; reason only from provided tool results."
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeIncomingMessages(messages: ChatMessage[], userLocation?: string, params: Pick<ToolAwareCompletionParams, "enabledTools"> = {}) {
|
|
|
|
|
const normalized = messages.map((message) => buildOpenAIConversationMessage(message));
|
|
|
|
|
|
|
|
|
|
return [{ role: "system", content: CHAT_TOOL_SYSTEM_PROMPT }, buildSystemPromptAugmentationMessage(userLocation), ...normalized];
|
|
|
|
|
return [{ role: "system", content: buildChatToolSystemPrompt(params) }, buildSystemPromptAugmentationMessage(userLocation), ...normalized];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizePlainIncomingMessages(messages: ChatMessage[], userLocation?: string) {
|
|
|
|
|
return [buildSystemPromptAugmentationMessage(userLocation), ...messages.map((message) => buildOpenAIConversationMessage(message))];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeIncomingResponsesInput(messages: ChatMessage[], userLocation?: string) {
|
|
|
|
|
function normalizeIncomingResponsesInput(messages: ChatMessage[], userLocation?: string, params: Pick<ToolAwareCompletionParams, "enabledTools"> = {}) {
|
|
|
|
|
const normalized = messages.map((message) => buildOpenAIResponsesInputMessage(message));
|
|
|
|
|
|
|
|
|
|
return [{ role: "system", content: CHAT_TOOL_SYSTEM_PROMPT }, buildSystemPromptAugmentationMessage(userLocation), ...normalized];
|
|
|
|
|
return [{ role: "system", content: buildChatToolSystemPrompt(params) }, buildSystemPromptAugmentationMessage(userLocation), ...normalized];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function runExaWebSearchTool(args: WebSearchArgs): Promise<ToolRunOutcome> {
|
|
|
|
|
@@ -962,7 +1018,8 @@ async function executeToolCallAndBuildEvent(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function runToolAwareOpenAIChat(params: ToolAwareCompletionParams): Promise<ToolAwareCompletionResult> {
|
|
|
|
|
const input: any[] = normalizeIncomingResponsesInput(params.messages, params.userLocation);
|
|
|
|
|
const enabledTools = getEnabledChatTools(params);
|
|
|
|
|
const input: any[] = normalizeIncomingResponsesInput(params.messages, params.userLocation, params);
|
|
|
|
|
const rawResponses: unknown[] = [];
|
|
|
|
|
const toolEvents: ToolExecutionEvent[] = [];
|
|
|
|
|
const usageAcc: Required<ToolAwareUsage> = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
|
|
|
@@ -976,7 +1033,7 @@ export async function runToolAwareOpenAIChat(params: ToolAwareCompletionParams):
|
|
|
|
|
input,
|
|
|
|
|
temperature: params.temperature,
|
|
|
|
|
max_output_tokens: params.maxTokens,
|
|
|
|
|
tools: RESPONSES_CHAT_TOOLS,
|
|
|
|
|
tools: toResponsesChatTools(enabledTools),
|
|
|
|
|
tool_choice: "auto",
|
|
|
|
|
parallel_tool_calls: true,
|
|
|
|
|
// Tool loops pass response output items back as input; reasoning items need persistence.
|
|
|
|
|
@@ -1031,7 +1088,8 @@ export async function runToolAwareOpenAIChat(params: ToolAwareCompletionParams):
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function runToolAwareChatCompletions(params: ToolAwareCompletionParams): Promise<ToolAwareCompletionResult> {
|
|
|
|
|
const conversation: any[] = normalizeIncomingMessages(params.messages, params.userLocation);
|
|
|
|
|
const enabledTools = getEnabledChatTools(params);
|
|
|
|
|
const conversation: any[] = normalizeIncomingMessages(params.messages, params.userLocation, params);
|
|
|
|
|
const rawResponses: unknown[] = [];
|
|
|
|
|
const toolEvents: ToolExecutionEvent[] = [];
|
|
|
|
|
const usageAcc: Required<ToolAwareUsage> = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
|
|
|
@@ -1045,7 +1103,7 @@ export async function runToolAwareChatCompletions(params: ToolAwareCompletionPar
|
|
|
|
|
messages: conversation,
|
|
|
|
|
temperature: params.temperature,
|
|
|
|
|
max_tokens: params.maxTokens,
|
|
|
|
|
tools: CHAT_TOOLS,
|
|
|
|
|
tools: enabledTools,
|
|
|
|
|
tool_choice: "auto",
|
|
|
|
|
} as any);
|
|
|
|
|
rawResponses.push(completion);
|
|
|
|
|
@@ -1139,7 +1197,8 @@ export async function runPlainChatCompletions(params: ToolAwareCompletionParams)
|
|
|
|
|
export async function* runToolAwareOpenAIChatStream(
|
|
|
|
|
params: ToolAwareCompletionParams
|
|
|
|
|
): AsyncGenerator<ToolAwareStreamingEvent> {
|
|
|
|
|
const input: any[] = normalizeIncomingResponsesInput(params.messages, params.userLocation);
|
|
|
|
|
const enabledTools = getEnabledChatTools(params);
|
|
|
|
|
const input: any[] = normalizeIncomingResponsesInput(params.messages, params.userLocation, params);
|
|
|
|
|
const rawResponses: unknown[] = [];
|
|
|
|
|
const toolEvents: ToolExecutionEvent[] = [];
|
|
|
|
|
const usageAcc: Required<ToolAwareUsage> = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
|
|
|
@@ -1153,7 +1212,7 @@ export async function* runToolAwareOpenAIChatStream(
|
|
|
|
|
input,
|
|
|
|
|
temperature: params.temperature,
|
|
|
|
|
max_output_tokens: params.maxTokens,
|
|
|
|
|
tools: RESPONSES_CHAT_TOOLS,
|
|
|
|
|
tools: toResponsesChatTools(enabledTools),
|
|
|
|
|
tool_choice: "auto",
|
|
|
|
|
parallel_tool_calls: true,
|
|
|
|
|
// Tool loops pass response output items back as input; reasoning items need persistence.
|
|
|
|
|
@@ -1265,7 +1324,8 @@ export async function* runToolAwareOpenAIChatStream(
|
|
|
|
|
export async function* runToolAwareChatCompletionsStream(
|
|
|
|
|
params: ToolAwareCompletionParams
|
|
|
|
|
): AsyncGenerator<ToolAwareStreamingEvent> {
|
|
|
|
|
const conversation: any[] = normalizeIncomingMessages(params.messages, params.userLocation);
|
|
|
|
|
const enabledTools = getEnabledChatTools(params);
|
|
|
|
|
const conversation: any[] = normalizeIncomingMessages(params.messages, params.userLocation, params);
|
|
|
|
|
const rawResponses: unknown[] = [];
|
|
|
|
|
const toolEvents: ToolExecutionEvent[] = [];
|
|
|
|
|
const usageAcc: Required<ToolAwareUsage> = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
|
|
|
@@ -1279,7 +1339,7 @@ export async function* runToolAwareChatCompletionsStream(
|
|
|
|
|
messages: conversation,
|
|
|
|
|
temperature: params.temperature,
|
|
|
|
|
max_tokens: params.maxTokens,
|
|
|
|
|
tools: CHAT_TOOLS,
|
|
|
|
|
tools: enabledTools,
|
|
|
|
|
tool_choice: "auto",
|
|
|
|
|
stream: true,
|
|
|
|
|
stream_options: { include_usage: true },
|
|
|
|
|
|