V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
cfer
V2EX  ›  Cloudflare

白嫖 worker AI 的进

  •  1
     
  •   cfer · 4 天前 · 570 次点击

    我使用 workers + worker Ai 构建了一个兼容 openai 接口的适配器。但是只是实现了基本功能,现在需要完善工具调用功能。有大佬能帮我完善它嘛?

    export default {
      async fetch(request, env, ctx) {
        // 处理 CORS 预检请求
        if (request.method === 'OPTIONS') {
          return new Response(null, {
            status: 200,
            headers: {
              'Access-Control-Allow-Origin': '*',
              'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
              'Access-Control-Allow-Headers': 'Content-Type, Authorization',
              'Access-Control-Max-Age': '86400',
            },
          });
        }
    
        // 验证 API Key
        const authHeader = request.headers.get('Authorization');
        if (!authHeader || !authHeader.startsWith('Bearer ')) {
          return new Response(JSON.stringify({
            error: { message: 'API key required', type: 'invalid_request_error', code: 'invalid_api_key' }
          }), { status: 401, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } });
        }
    
        const apiKey = authHeader.substring(7);
        const validApiKeys = env.VALID_API_KEYS ? env.VALID_API_KEYS.split(',') : ['your-api-key-here'];
        if (!validApiKeys.includes(apiKey)) {
          return new Response(JSON.stringify({
            error: { message: 'Invalid API key', type: 'invalid_request_error', code: 'invalid_api_key' }
          }), { status: 401, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } });
        }
    
        const url = new URL(request.url);
    
        // 模型映射
        const modelMap = {
          'deepseek-r1': '@cf/deepseek-ai/deepseek-r1-distill-qwen-32b',
          'gpt-oss-120b': '@cf/openai/gpt-oss-120b',
          'gpt-oss-20b': '@cf/openai/gpt-oss-20b',
          'llama-4-scout': '@cf/meta/llama-4-scout-17b-16e-instruct',
          'qwen32b': '@cf/qwen/qwq-32b',
          'gemma-3': '@cf/google/gemma-3-12b-it',
          'qwen3-embedding-0.6b': '@cf/qwen/qwen3-embedding-0.6b'
        };
    
        // 聊天接口
        if (url.pathname === '/v1/chat/completions' && request.method === 'POST') {
          try {
            const body = await request.json();
            if (!body.messages || !Array.isArray(body.messages)) {
              return new Response(JSON.stringify({
                error: { message: 'Messages must be an array', type: 'invalid_request_error', code: 'invalid_parameter' }
              }), { status: 400, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } });
            }
    
            const model = body.model || 'deepseek-r1';
            const cfModel = modelMap[model];
            if (!cfModel) {
              return new Response(JSON.stringify({
                error: { message: `Model '${model}' not supported`, type: 'invalid_request_error', code: 'model_not_found' }
              }), { status: 400, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } });
            }
    
            // 构造 AI 请求参数
            let aiRequest = {};
            let useResponsesAPI = cfModel.startsWith('@cf/openai/gpt-oss');
    
            if (useResponsesAPI) {
              // Responses API 格式
              const systemMsg = body.messages.find(m => m.role === 'system')?.content || "You are a helpful assistant.";
              const userMsgs = body.messages.filter(m => m.role === 'user').map(m => m.content).join("\n");
    
              aiRequest = {
                input: userMsgs,
                instructions: systemMsg,
                temperature: body.temperature ?? 0.7,
                top_p: body.top_p ?? 0.9,
                max_tokens: body.max_tokens ?? 2048,
                reasoning: body.reasoning ?? { effort: "medium" }
              };
            } else {
              // 旧模型:拼接 prompt
              let prompt = '';
              for (const message of body.messages) {
                if (message.role === 'system') prompt += `System: ${message.content}\n\n`;
                if (message.role === 'user') prompt += `User: ${message.content}\n\n`;
                if (message.role === 'assistant') prompt += `Assistant: ${message.content}\n\n`;
              }
              prompt += 'Assistant: ';
    
              aiRequest = {
                prompt,
                temperature: body.temperature ?? 0.7,
                top_p: body.top_p ?? 0.9,
                max_tokens: body.max_tokens ?? 4096,
              };
            }
    
            // 调用 Cloudflare AI
            const response = await env.AI.run(cfModel, aiRequest);
    
            const completionId = 'chatcmpl-' + Math.random().toString(36).substring(2, 15);
            const timestamp = Math.floor(Date.now() / 1000);
    
            // 获取最终回答内容
            let assistantContent = "";
            if (useResponsesAPI) {
              if (response.output && Array.isArray(response.output)) {
                assistantContent = response.output
                  .flatMap(msg => msg.content
                    .filter(c => c.type === "output_text")
                    .map(c => c.text)
                  )
                  .join("\n");
              }
            } else {
              assistantContent = response.response ?? "";
            }
            
    
            // 流式输出
            if (body.stream) {
              const encoder = new TextEncoder();
              const stream = new ReadableStream({
                start(controller) {
                  // 开始事件
                  controller.enqueue(encoder.encode(`data: ${JSON.stringify({
                    id: completionId,
                    object: 'chat.completion.chunk',
                    created: timestamp,
                    model,
                    choices: [{ index: 0, delta: { role: 'assistant', content: "" }, finish_reason: null }]
                  })}\n\n`));
    
                  // 模拟逐块输出
                  const chunkSize = 20;
                  for (let i = 0; i < assistantContent.length; i += chunkSize) {
                    const chunk = assistantContent.slice(i, i + chunkSize);
                    controller.enqueue(encoder.encode(`data: ${JSON.stringify({
                      id: completionId,
                      object: 'chat.completion.chunk',
                      created: timestamp,
                      model,
                      choices: [{ index: 0, delta: { content: chunk }, finish_reason: null }]
                    })}\n\n`));
                  }
    
                  // 结束事件
                  controller.enqueue(encoder.encode(`data: ${JSON.stringify({
                    id: completionId,
                    object: 'chat.completion.chunk',
                    created: timestamp,
                    model,
                    choices: [{ index: 0, delta: {}, finish_reason: 'stop' }]
                  })}\n\n`));
                  controller.enqueue(encoder.encode('data: [DONE]\n\n'));
                  controller.close();
                }
              });
              return new Response(stream, {
                headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Access-Control-Allow-Origin': '*' },
              });
            }
    
            // 非流式输出
            const chatCompletion = {
              id: completionId,
              object: 'chat.completion',
              created: timestamp,
              model,
              choices: [{ index: 0, message: { role: 'assistant', content: assistantContent }, finish_reason: 'stop' }],
              usage: {
                prompt_tokens: Math.ceil(JSON.stringify(body.messages).length / 4),
                completion_tokens: Math.ceil(assistantContent.length / 4),
                total_tokens: Math.ceil((JSON.stringify(body.messages).length + assistantContent.length) / 4)
              }
            };
            return new Response(JSON.stringify(chatCompletion), { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } });
    
          } catch (error) {
            console.error('Error:', error);
            return new Response(JSON.stringify({
              error: { message: 'Internal server error', type: 'server_error', code: 'internal_error' }
            }), { status: 500, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } });
          }
        }
        // 嵌入模型
        if (url.pathname === '/v1/embeddings' && request.method === 'POST') {
          try {
            const body = await request.json();
            if (!body.input) {
              return new Response(JSON.stringify({
                error: { message: 'Input is required', type: 'invalid_request_error', code: 'invalid_parameter' }
              }), { status: 400, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } });
            }
        
            const model = body.model;
            const cfModel = modelMap[model];
            if (!cfModel) {
              return new Response(JSON.stringify({
                error: { message: `Model '${model}' not supported`, type: 'invalid_request_error', code: 'model_not_found' }
              }), { status: 400, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } });
            }
        
            // 调用 AI 获取 embedding
            let embeddingResponse;
    
            // Responses API 假设支持 embedding
            embeddingResponse = await env.AI.run(cfModel, {
              text: body.input
            });
            
            // 构造返回
            const embedding = embeddingResponse.embedding || [0]; // 如果返回格式不同,需要根据实际结果调整
            return new Response(JSON.stringify(embeddingResponse), { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } });
        
          } catch (error) {
            console.error('Embedding error:', error);
            return new Response(JSON.stringify({
              error: { message: 'Internal server error', type: 'server_error', code: 'internal_error' }
            }), { status: 500, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } });
          }
        }
    
        // 模型列表
        if (url.pathname === '/v1/models' && request.method === 'GET') {
          const models = Object.keys(modelMap).map(id => ({
            id,
            object: 'model',
            created: Math.floor(Date.now() / 1000),
            owned_by: 'cloudflare',
            permission: [{ id: 'modelperm-' + id, object: 'model_permission', created: Math.floor(Date.now() / 1000), allow_create_engine: false, allow_sampling: true, allow_logprobs: false, allow_search_indices: false, allow_view: true, allow_fine_tuning: false, organization: '*', group: null, is_blocking: false }]
          }));
          return new Response(JSON.stringify({ object: 'list', data: models }), { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } });
        }
    
        // 健康检查
        if (url.pathname === '/health' && request.method === 'GET') {
          return new Response(JSON.stringify({ status: 'healthy', timestamp: new Date().toISOString(), models: Object.keys(modelMap) }), { headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } });
        }
    
        // 404
        return new Response(JSON.stringify({
          error: { message: 'Not found', type: 'invalid_request_error', code: 'not_found' }
        }), { status: 404, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } });
      },
    };
    
    1 条回复    2025-12-25 17:01:44 +08:00
    goodhunt
        1
    goodhunt  
       2 天前
    可以试试问问大模型
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   2572 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 03:07 · PVG 11:07 · LAX 19:07 · JFK 22:07
    ♥ Do have faith in what you're doing.