白嫖 worker AI 的进

2025 年 12 月 24 日
 cfer

我使用 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': '*' } });
  },
};
1070 次点击
所在节点    Cloudflare
1 条回复
goodhunt
2025 年 12 月 25 日
可以试试问问大模型

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://study.congcong.us/t/1180851

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX