genaiscript踩坑:設定proxyman抓包、相容qwen72b funtion-call

索美不达米亚發表於2024-11-19

genaiscript有個很棒的日誌系統,但是碰到介面報錯就沒用了,還是得抓包來看,為了設定proxy,得修改原始碼。genaiscript是透過npx執行的,包的執行優先順序是本地依賴目錄npm install genaiscript——npm全域性依賴目錄npm install -g genaiscript——npx快取目錄從沒有安裝過本地包,在Mac上對應的資料夾路徑是 /你的本地專案/node_modules/genaiscript/built/genaiscript.cjs——/Users/username/.nvm/versions/node/v20.15.1/lib/node_modules/genaiscript/built/genaiscript.cjs——/Users/username/.npm/_npx/86ce7b375aee60ee/node_modules/genaiscript/built/genaiscript.cjs,注意如果執行npx --yes genaiscript@1.75.3 run demo,如果你本地或者全域性安裝的版本不是@1.75.3,那npm會自動下載genaiscript@1.75.3到npx快取目錄執行,所以版本號也要一致。genaiscript.cjs是一個快10萬行的打包js檔案,好在程式碼是非壓縮的,可以方便地直接修改。我修改的版本是@1.75.3

// genaiscript.cjs
// ../core/src/openai.ts
function getConfigHeaders(cfg) {
  let { token, type, base } = cfg;
	// ... existing code ...
  const fetchHeaders = {
    ...getConfigHeaders(cfg),
    "Content-Type": "application/json",
    ...headers || {}
  };
  traceFetchPost(trace, url, fetchHeaders, postReq);
  const body = JSON.stringify(postReq);
  let r2;
+  // 增加代理抓包,僅針對OpenAI介面的請求
+  const { HttpsProxyAgent } = require('https-proxy-agent');
+  const proxy = 'http://192.168.8.110:9090';
+  const agent = new HttpsProxyAgent(proxy);
  try {
    r2 = await fetchRetry(url, {
+      agent:agent,
      headers: fetchHeaders,
      body,
      method: "POST",
      signal: toSignal(cancellationToken),
      ...rest || {}
    });
// ... existing code ...
+  let toolCall_ids = new Set();
  const doChoices = (json, tokens) => {
    const obj = JSON.parse(json);
    if (!postReq.stream) trace.detailsFenced(`response`, obj, "json");
    if (obj.usage) usage = obj.usage;
    if (!responseModel && obj.model) responseModel = obj.model;
    if (!obj.choices?.length) return;
    else if (obj.choices?.length != 1)
      throw new Error("too many choices in response");
    const choice = obj.choices[0];
    const { finish_reason } = choice;
    if (finish_reason) finishReason = finish_reason;
    if (choice.delta) {
      const { delta, logprobs } = choice;
      if (logprobs?.content) lbs.push(...logprobs.content);
      if (typeof delta?.content === "string" && delta?.content !== "") {
        numTokens += estimateTokens(delta.content, encoder);
        chatResp += delta.content;
        tokens.push(
          ...serializeChunkChoiceToLogProbs(choice)
        );
        trace.appendToken(delta.content);
      } else if (Array.isArray(delta.tool_calls)) {
        const { tool_calls } = delta;
        for (const call of tool_calls) {
+          // 相容siliconflow, aliyun的qwen72b呼叫index不正確
+          let id = call.id;
+          if (typeof id == 'string' && id.length > 0 && !toolCall_ids.has(id)) {
+            toolCall_ids.add(id);
+          }
+          let uniq_index = toolCall_ids.size - 1

+          const tc = toolCalls[uniq_index] || (toolCalls[uniq_index] = {
-          const tc = toolCalls[call.index] || (toolCalls[call.index] = {
            id: call.id,
            name: call.function.name,
            arguments: ""
          });
          if (call.function.arguments)
            tc.arguments += call.function.arguments;
          // console.log(JSON.stringify(toolCalls))
        }
      }