← 返回聊天
新建
删除
Models
gpt5.php
gpt5_file.php
gpt5_mini_file.php
openai_chat.php
Tools
get_time.php
get_weather.php
global_search_messages.php
math.php
memo.php
news.php
search_arxiv.php
search_crossref.php
search_github_code.php
search_pubmed.php
search_semantic_scholar.php
stock_market.php
url.php
<?php declare(strict_types=1); $BASE_URL = 'https://api.openai.com/v1'; $API_KEY = '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'; $MODEL_NAME = 'gpt-5-chat-latest'; $DEFAULTS = [ 'temperature' => 0.2, 'max_tokens' => 2048, ]; return [ 'id' => 'gpt5', 'label' => 'gpt5', 'supports_tools' => true, 'call' => function(array $messages, array $tools = [], array $options = []) use ($BASE_URL, $API_KEY, $MODEL_NAME, $DEFAULTS): array { // ✅ Responses API endpoint $endpoint = rtrim($BASE_URL, '/\\') . '/responses'; $extraSystem = [ 'role' => 'system', 'content' => '回答请更完整:给出步骤、要点、必要时补充边界条件与注意事项。', ]; $outMessages = $messages; array_splice($outMessages, 1, 0, [$extraSystem]); // 1) 把 system 合并进 instructions(更稳,避免 role=system 的各种差异) $instructionsParts = []; $chatLike = []; foreach ($outMessages as $m) { if (!is_array($m)) continue; $role = (string)($m['role'] ?? 'user'); if ($role === 'system') { $instructionsParts[] = (string)($m['content'] ?? ''); } else { $chatLike[] = $m; } } $instructions = trim(implode("\n\n", array_filter($instructionsParts))); // 2) messages -> responses input items $input = []; foreach ($chatLike as $m) { $role = (string)($m['role'] ?? 'user'); // tool 输出:role=tool -> function_call_output item(需要 tool_call_id) if ($role === 'tool') { $callId = (string)($m['tool_call_id'] ?? $m['call_id'] ?? ''); $out = $m['content'] ?? ''; $outStr = is_string($out) ? $out : json_encode($out, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); if ($callId !== '') { $input[] = [ 'type' => 'function_call_output', 'call_id' => $callId, 'output' => $outStr, ]; } else { // 没有 call_id 时兜底成 user 文本,保证链路不崩 $input[] = [ 'type' => 'message', 'role' => 'user', 'content' => [ ['type' => 'input_text', 'text' => "[tool output]\n" . $outStr], ], ]; } continue; } // assistant 带 tool_calls:转成 function_call items(保持 call_id) if ($role === 'assistant' && isset($m['tool_calls']) && is_array($m['tool_calls'])) { foreach ($m['tool_calls'] as $tc) { if (!is_array($tc)) continue; $fn = $tc['function'] ?? []; $callId = (string)($tc['id'] ?? $tc['call_id'] ?? ('call_' . bin2hex(random_bytes(6)))); $name = (string)($fn['name'] ?? ''); $args = $fn['arguments'] ?? ''; $argsStr = is_string($args) ? $args : json_encode($args, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); if ($name !== '') { $input[] = [ 'type' => 'function_call', 'call_id' => $callId, 'name' => $name, 'arguments' => $argsStr, ]; } } // assistant 的可见文本(如果有)也补一条 message(注意 assistant 需 output_text) $txt = (string)($m['content'] ?? ''); if ($txt !== '') { $input[] = [ 'type' => 'message', 'role' => 'assistant', 'content' => [ ['type' => 'output_text', 'text' => $txt], ], ]; } continue; } // 普通 message:role=user/assistant/developer $txt = $m['content'] ?? ''; $txtStr = is_string($txt) ? $txt : json_encode($txt, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $contentType = ($role === 'assistant') ? 'output_text' : 'input_text'; $input[] = [ 'type' => 'message', 'role' => $role, 'content' => [ ['type' => $contentType, 'text' => $txtStr], ], ]; } // 3) tools:ChatCompletions 形状 -> Responses 形状(平铺 name/parameters) $normalizedTools = []; if (!empty($tools)) { foreach ($tools as $t) { if (!is_array($t)) continue; if (($t['type'] ?? '') !== 'function') continue; if (isset($t['function']) && is_array($t['function'])) { $fn = $t['function']; $normalizedTools[] = [ 'type' => 'function', 'name' => (string)($fn['name'] ?? ''), 'description' => (string)($fn['description'] ?? ''), 'parameters' => $fn['parameters'] ?? ['type' => 'object', 'properties' => new stdClass()], 'strict' => (bool)($fn['strict'] ?? false), ]; } else { // 已经是 Responses 形状 $normalizedTools[] = $t; } } } $payload = [ 'model' => (string)($options['model'] ?? $MODEL_NAME), 'input' => $input, 'temperature' => $options['temperature'] ?? $DEFAULTS['temperature'], 'max_output_tokens' => $options['max_tokens'] ?? $DEFAULTS['max_tokens'], ]; if ($instructions !== '') $payload['instructions'] = $instructions; if (!empty($normalizedTools)) { $payload['tools'] = $normalizedTools; $payload['tool_choice'] = 'auto'; $payload['parallel_tool_calls'] = true; } $headers = ['Content-Type: application/json']; if (trim($API_KEY) !== '') $headers[] = 'Authorization: Bearer ' . $API_KEY; $ch = curl_init($endpoint); if ($ch === false) return ['ok' => false, 'error' => 'curl_init failed']; $json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => $json, CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 120, ]); $resp = curl_exec($ch); $errno = curl_errno($ch); $err = curl_error($ch); $status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($errno !== 0) return ['ok' => false, 'error' => 'cURL error: ' . $err]; $data = json_decode((string)$resp, true); if (!is_array($data)) return ['ok' => false, 'error' => 'Invalid JSON', 'raw' => (string)$resp]; if ($status < 200 || $status >= 300) { $msg = $data['error']['message'] ?? ('HTTP ' . $status); return ['ok' => false, 'error' => (string)$msg, 'raw' => $data]; } // 4) Responses output -> 原项目 message 结构(content + tool_calls) $assistantText = ''; $toolCalls = []; $output = $data['output'] ?? []; if (is_array($output)) { foreach ($output as $item) { if (!is_array($item)) continue; if (($item['type'] ?? '') === 'message' && ($item['role'] ?? '') === 'assistant') { $content = $item['content'] ?? []; if (is_array($content)) { foreach ($content as $c) { if (is_array($c) && ($c['type'] ?? '') === 'output_text') { $assistantText .= (string)($c['text'] ?? ''); } if (is_array($c) && ($c['type'] ?? '') === 'refusal') { $assistantText .= (string)($c['refusal'] ?? ''); } } } } if (($item['type'] ?? '') === 'function_call') { $toolCalls[] = [ 'id' => (string)($item['call_id'] ?? ('call_' . bin2hex(random_bytes(6)))), 'type' => 'function', 'function' => [ 'name' => (string)($item['name'] ?? ''), 'arguments' => (string)($item['arguments'] ?? ''), ], ]; } } } $msg = [ 'role' => 'assistant', 'content' => $assistantText, ]; if (!empty($toolCalls)) { $msg['tool_calls'] = $toolCalls; } return ['ok' => true, 'message' => $msg, 'raw' => $data, 'status' => $status]; }, ];
保存