feat(research): Implement research APIs.

This commit is contained in:
2025-10-03 12:50:42 +00:00
parent e34bf19698
commit fe8540c8ba
11 changed files with 367 additions and 114 deletions

View File

@@ -233,52 +233,37 @@ export class OpenAiProvider extends MultiModalModel {
}
public async research(optionsArg: ResearchOptions): Promise<ResearchResponse> {
// Determine which model to use based on search depth
// Determine which model to use - Deep Research API requires specific models
let model: string;
if (optionsArg.searchDepth === 'deep') {
model = this.options.researchModel || 'o4-mini-deep-research-2025-06-26';
} else {
model = this.options.chatModel || 'gpt-5-mini';
// For basic/advanced, still use deep research models if web search is needed
if (optionsArg.includeWebSearch) {
model = this.options.researchModel || 'o4-mini-deep-research-2025-06-26';
} else {
model = this.options.chatModel || 'gpt-5-mini';
}
}
// Prepare the request parameters
const systemMessage = 'You are a research assistant. Provide comprehensive answers with citations and sources when available.';
// Prepare request parameters using Deep Research API format
const requestParams: any = {
model,
messages: [
{
role: 'system',
content: 'You are a research assistant. Provide comprehensive answers with citations and sources when available.'
},
{
role: 'user',
content: optionsArg.query
}
],
temperature: 0.7
instructions: systemMessage,
input: optionsArg.query
};
// Add web search tools if requested
// Add web search tool if requested
if (optionsArg.includeWebSearch || optionsArg.searchDepth === 'deep') {
requestParams.tools = [
{
type: 'function',
function: {
name: 'web_search',
description: 'Search the web for information',
parameters: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'The search query'
}
},
required: ['query']
}
}
type: 'web_search_preview',
search_context_size: optionsArg.searchDepth === 'deep' ? 'high' :
optionsArg.searchDepth === 'advanced' ? 'medium' : 'low'
}
];
requestParams.tool_choice = 'auto';
}
// Add background flag for deep research
@@ -287,14 +272,36 @@ export class OpenAiProvider extends MultiModalModel {
}
try {
// Execute the research request
const result = await this.openAiApiClient.chat.completions.create(requestParams);
// Execute the research request using Deep Research API
const result = await this.openAiApiClient.responses.create(requestParams);
// Extract the answer
const answer = result.choices[0].message.content || '';
// Parse sources from the response (OpenAI often includes URLs in markdown format)
// Extract the answer from output items
let answer = '';
const sources: Array<{ url: string; title: string; snippet: string }> = [];
const searchQueries: string[] = [];
// Process output items
for (const item of result.output || []) {
// Extract message content
if (item.type === 'message' && 'content' in item) {
const messageItem = item as any;
for (const contentItem of messageItem.content || []) {
if (contentItem.type === 'output_text' && 'text' in contentItem) {
answer += contentItem.text;
}
}
}
// Extract web search queries
if (item.type === 'web_search_call' && 'action' in item) {
const searchItem = item as any;
if (searchItem.action && searchItem.action.type === 'search' && 'query' in searchItem.action) {
searchQueries.push(searchItem.action.query);
}
}
}
// Parse sources from markdown links in the answer
const urlRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
let match: RegExpExecArray | null;
@@ -302,27 +309,10 @@ export class OpenAiProvider extends MultiModalModel {
sources.push({
title: match[1],
url: match[2],
snippet: '' // OpenAI doesn't provide snippets in standard responses
snippet: ''
});
}
// Extract search queries if tools were used
const searchQueries: string[] = [];
if (result.choices[0].message.tool_calls) {
for (const toolCall of result.choices[0].message.tool_calls) {
if ('function' in toolCall && toolCall.function.name === 'web_search') {
try {
const args = JSON.parse(toolCall.function.arguments);
if (args.query) {
searchQueries.push(args.query);
}
} catch (e) {
// Ignore parsing errors
}
}
}
}
return {
answer,
sources,