惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
Last Week in AI
Last Week in AI
IT之家
IT之家
A
About on SuperTechFans
M
MIT News - Artificial intelligence
Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 三生石上(FineUI控件)
博客园 - 【当耐特】
V
Visual Studio Blog
Microsoft Security Blog
Microsoft Security Blog
博客园_首页
aimingoo的专栏
aimingoo的专栏
The Cloudflare Blog
Vercel News
Vercel News
博客园 - Franky
有赞技术团队
有赞技术团队
B
Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
GbyAI
GbyAI
量子位
云风的 BLOG
云风的 BLOG
T
Tailwind CSS Blog

V2EX

我用 AI 写代码,但终端管理反而成了累赘——于是我做了 codux [调研] 各位在公司都用什么 ide 和 agent 写代码? 老运维 share 一个运维平台 新电脑 brew install node 之后,一个小设置可以提升对供应链投毒的防御 GLM-Coding 调用持续报错: z.ai 的 Lite 套餐几乎无法使用,官方 Pro/Max 是否稳定? 上海漕河泾内推,本组有 2 个 hc,一个后端,一个前端,预算都是 20k 左右,不打卡,氛围好 如果 V2EX 上有一组不永久保存聊天记录(比如只保存 7 天或者 24 小时)的聊天室,那么会开启哪些有用或者有趣的可能? gemini cli 貌似挂了,一直返回 403 第一次在自媒体上赚到钱 收集了最近在使用的低价 GPT, Gemini,邮箱等 AI 会员的小店合集 讨论个大实话:现在企业还在说 AI 编程提效 20%, 30%的,真的太落后,没用懂 AI。因为包括很多前沿公司,已经狂奔到提效 200%-500%的情况 [招聘][远程][币安] 前端/后端/QA/iOS/Android 至少 3 年以上经验 目前有大量 HC 欢迎投递 Chatgpt Pro 用量用不完的可以开这些设置 面试的时候好像遇到钓鱼了,给各位避个坑 cursor 年续费 22 号到期, 自动续费是否还是老的计次套餐呢 被两件破事毁掉的一下午,琐碎的内耗消磨人的精力 使用 Planet 存储 Codex 的会话或者重要信息 如果业务部门领导不要你开发功能,而是要求你教会它用 claude code 开发功能,你会怎么做? 分享一个 MacOS 接绿联 CM818 USB 转 DP 转接器使用感受 我的 HR 朋友 10 年老 Java ,非全大专,大家帮忙看看简历 开源了一个 AI 口语练习工具,音素级发音评分,完全免费可自部署 V2EX 上有哪些你觉得很有趣、印象深刻的妹纸? 字节为啥不出个国内版 Vercel? 有在大马的朋友吗? 问个运营商问题 你们在有领导的公司大群发过的最大胆的消息是什么 公司裁员,目前没有工作。想试试摆摊,做一个移动鲜啤打酒车 我的硬盘 Memblaze Pblaze 5 Linux 下不识别,给 Linux 内核提交了补丁, AI 说有望被合并 只有我一个人觉得 codex 不好用? 做了个 AI + 真人专家监督的广告投放平台 Auxora, 7 个品牌跑出 6x ROAS
白嫖 worker AI 的进
cfer · 2025-12-24 · via V2EX

我使用 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': '*' } });
  },
};