@dahawa/hawa-cli-analysis 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/clogger-openai.js CHANGED
@@ -1,190 +1,190 @@
1
- import {mergeAnthropic} from './api-anthropic.js';
2
- import LoggerManage from "./logger-manager.js"
3
- import { URL } from 'url';
4
- import anthropicTransformer from "./anthropic-transformer.js"
5
- import {parseOpenAIChatCompletion} from "./api-openai.js";
6
- let logger = LoggerManage.getLogger("claudecode");
7
-
8
- logger.system.debug("-------------Clogger Start--------------------------");
9
-
10
- function deepClone(obj) {
11
- const result = JSON.parse(JSON.stringify(obj));
12
- return result;
13
- }
14
- function formateLine(str){
15
- let r = str.replace(/\\n/g, '\n');
16
- return r;
17
- }
18
- function toSimpleLog(full, wire_api = "chat"){
19
- let log = {
20
- request:{},
21
- response:{},
22
- openai:{
23
- request:{},
24
- response:{}
25
- }
26
- }
27
- //anthropic 转换
28
- log.request.model = full.request.body.model;
29
- log.request.messages = full.request.body.messages;
30
- log.request.system = full.request.body.system;
31
- log.response.content = full.response.body.content;
32
-
33
- // openai 转换
34
- log.openai.request.model = full.openai.request.body.model;
35
- log.openai.request.messages = full.openai.request.body.messages;
36
- log.openai.response.choices = full.openai.response.body.choices;
37
-
38
-
39
- return log;
40
- }
41
-
42
- function logAPI(fullLog){
43
- logger.full.debug(fullLog);
44
- logger.simple.debug(toSimpleLog(fullLog));
45
- //要及时输出
46
- logger.simple.flush();
47
- logger.full.flush();
48
- }
49
-
50
-
51
- function headersToObject(headers) {
52
- const obj = {};
53
- try {
54
- for (const [k, v] of headers.entries()) obj[k] = v;
55
- } catch {}
56
- return obj;
57
- }
58
-
59
- /**
60
- *
61
- *
62
- * 拦截 claude code 请求,这里应该拦截模型情况。当前拦截了所有的请求,模型请求可以通过 endpoint 判断出来
63
- * 当前的逻辑需要优化
64
- *
65
- */
66
- function instrumentFetch() {
67
- if (!global.fetch || global.fetch.__ProxyInstrumented) return;
68
-
69
- logger.system.debug("-------------Clogger instrumentFetch--------------------------");
70
-
71
- const originalFetch = global.fetch;
72
- global.fetch = async (input, init = {}) => {
73
- const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;
74
- const endpoints = [
75
- '/v1/messages'
76
- ];
77
- let urlPath = (new URL(url)).pathname;
78
-
79
- if(!(endpoints.some(t => urlPath.includes(t) && init.method == "POST"))){
80
- //console.log("不是模型请求直接返回" +init.method +":" + url +" -> " + urlPath);
81
- return originalFetch(input,init);
82
- }
83
-
84
- //console.log("请求地址: " + url);
85
- //转换前的请求
86
- let initBody = JSON.parse(init.body);
87
- //请求的 JSON
88
- let requestBody = await anthropicTransformer.transformRequestOut(initBody);
89
- //转换后的请求
90
- let openaiRequestBodyString = JSON.stringify(requestBody);
91
-
92
- //console.log(JSON.parse(openaiRequestBody));
93
-
94
- //打印请求信息 init.body
95
- const response = await originalFetch("https://openrouter.ai/api/v1/chat/completions", {
96
- method: "POST",
97
- headers: {
98
- "Content-Type": "application/json",
99
- Authorization: `Bearer ${process.env.ANTHROPIC_AUTH_TOKEN}`
100
- },
101
- body: openaiRequestBodyString,
102
- });
103
-
104
- let responseToClient = response.clone();
105
-
106
- // 判断OpenRouter响应是否为异常
107
- if (!response.ok) {
108
- // 读取OpenRouter错误响应
109
- const openrouterErrorText = await response.text();
110
- logger.system.error(`OpenRouter API error response: ${response.status} ${response.statusText}`, {
111
- url: url,
112
- status: response.status,
113
- errorResponse: openrouterErrorText
114
- });
115
-
116
- // 将OpenRouter错误响应转换为Claude Code错误响应格式
117
- let claudeErrorResponse;
118
- try {
119
- const openrouterError = JSON.parse(openrouterErrorText);
120
- claudeErrorResponse = {
121
- type: "error",
122
- error: {
123
- type: "api_error",
124
- message: openrouterError.error?.message || `OpenRouter API error: ${response.statusText}`,
125
- code: `OPENROUTER_${response.status}_ERROR`
126
- }
127
- };
128
- } catch (parseError) {
129
- // 如果无法解析OpenRouter的错误JSON,使用通用错误格式
130
- claudeErrorResponse = {
131
- type: "error",
132
- error: {
133
- type: "api_error",
134
- message: `OpenRouter API error: ${response.statusText}`,
135
- code: `OPENROUTER_${response.status}_ERROR`
136
- }
137
- };
138
- }
139
-
140
- // 返回转换后的错误响应
141
- return new Response(JSON.stringify(claudeErrorResponse), {
142
- status: response.status,
143
- statusText: response.statusText,
144
- headers: {
145
- "Content-Type": "application/json"
146
- }
147
- });
148
- }
149
-
150
- //完整的请求日志,保护请求和响应
151
- let fullLog = {request:{
152
- url:url,
153
- method: init.method,
154
- headers: headersToObject(init.headers),
155
- body: initBody
156
- },response:{
157
- status: response.status,
158
- statusText: response.statusText,
159
- headers: headersToObject(response.headers)
160
- },openai:{
161
- request: {
162
- body: requestBody
163
- },
164
- response: {}
165
- }};
166
-
167
- let res = await anthropicTransformer.transformResponseIn(responseToClient);
168
- let toClientRes = await res.clone();
169
-
170
- (async () => {
171
-
172
- fullLog.openai.response.body = await parseOpenAIChatCompletion(await response.text());
173
- fullLog.response.body = mergeAnthropic(await res.text());
174
-
175
- //其他类型是错误的
176
- logAPI(fullLog);
177
-
178
- })().catch(err => console.error('日志解析错误:', err));
179
-
180
- return toClientRes;
181
-
182
-
183
- };
184
- global.fetch.__ProxyInstrumented = true;
185
- }
186
- try{
187
- instrumentFetch();
188
- }catch(e){
189
- logger.system.error(e);
190
- }
1
+ import {mergeAnthropic} from './api-anthropic.js';
2
+ import LoggerManage from "./logger-manager.js"
3
+ import { URL } from 'url';
4
+ import anthropicTransformer from "./anthropic-transformer.js"
5
+ import {parseOpenAIChatCompletion} from "./api-openai.js";
6
+ let logger = LoggerManage.getLogger("claudecode");
7
+
8
+ logger.system.debug("-------------Clogger Start--------------------------");
9
+
10
+ function deepClone(obj) {
11
+ const result = JSON.parse(JSON.stringify(obj));
12
+ return result;
13
+ }
14
+ function formateLine(str){
15
+ let r = str.replace(/\\n/g, '\n');
16
+ return r;
17
+ }
18
+ function toSimpleLog(full, wire_api = "chat"){
19
+ let log = {
20
+ request:{},
21
+ response:{},
22
+ openai:{
23
+ request:{},
24
+ response:{}
25
+ }
26
+ }
27
+ //anthropic 转换
28
+ log.request.model = full.request.body.model;
29
+ log.request.messages = full.request.body.messages;
30
+ log.request.system = full.request.body.system;
31
+ log.response.content = full.response.body.content;
32
+
33
+ // openai 转换
34
+ log.openai.request.model = full.openai.request.body.model;
35
+ log.openai.request.messages = full.openai.request.body.messages;
36
+ log.openai.response.choices = full.openai.response.body.choices;
37
+
38
+
39
+ return log;
40
+ }
41
+
42
+ function logAPI(fullLog){
43
+ logger.full.debug(fullLog);
44
+ logger.simple.debug(toSimpleLog(fullLog));
45
+ //要及时输出
46
+ logger.simple.flush();
47
+ logger.full.flush();
48
+ }
49
+
50
+
51
+ function headersToObject(headers) {
52
+ const obj = {};
53
+ try {
54
+ for (const [k, v] of headers.entries()) obj[k] = v;
55
+ } catch {}
56
+ return obj;
57
+ }
58
+
59
+ /**
60
+ *
61
+ *
62
+ * 拦截 claude code 请求,这里应该拦截模型情况。当前拦截了所有的请求,模型请求可以通过 endpoint 判断出来
63
+ * 当前的逻辑需要优化
64
+ *
65
+ */
66
+ function instrumentFetch() {
67
+ if (!global.fetch || global.fetch.__ProxyInstrumented) return;
68
+
69
+ logger.system.debug("-------------Clogger instrumentFetch--------------------------");
70
+
71
+ const originalFetch = global.fetch;
72
+ global.fetch = async (input, init = {}) => {
73
+ const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;
74
+ const endpoints = [
75
+ '/v1/messages'
76
+ ];
77
+ let urlPath = (new URL(url)).pathname;
78
+
79
+ if(!(endpoints.some(t => urlPath.includes(t) && init.method == "POST"))){
80
+ //console.log("不是模型请求直接返回" +init.method +":" + url +" -> " + urlPath);
81
+ return originalFetch(input,init);
82
+ }
83
+
84
+ //console.log("请求地址: " + url);
85
+ //转换前的请求
86
+ let initBody = JSON.parse(init.body);
87
+ //请求的 JSON
88
+ let requestBody = await anthropicTransformer.transformRequestOut(initBody);
89
+ //转换后的请求
90
+ let openaiRequestBodyString = JSON.stringify(requestBody);
91
+
92
+ //console.log(JSON.parse(openaiRequestBody));
93
+
94
+ //打印请求信息 init.body
95
+ const response = await originalFetch("https://openrouter.ai/api/v1/chat/completions", {
96
+ method: "POST",
97
+ headers: {
98
+ "Content-Type": "application/json",
99
+ Authorization: `Bearer ${process.env.ANTHROPIC_AUTH_TOKEN}`
100
+ },
101
+ body: openaiRequestBodyString,
102
+ });
103
+
104
+ let responseToClient = response.clone();
105
+
106
+ // 判断OpenRouter响应是否为异常
107
+ if (!response.ok) {
108
+ // 读取OpenRouter错误响应
109
+ const openrouterErrorText = await response.text();
110
+ logger.system.error(`OpenRouter API error response: ${response.status} ${response.statusText}`, {
111
+ url: url,
112
+ status: response.status,
113
+ errorResponse: openrouterErrorText
114
+ });
115
+
116
+ // 将OpenRouter错误响应转换为Claude Code错误响应格式
117
+ let claudeErrorResponse;
118
+ try {
119
+ const openrouterError = JSON.parse(openrouterErrorText);
120
+ claudeErrorResponse = {
121
+ type: "error",
122
+ error: {
123
+ type: "api_error",
124
+ message: openrouterError.error?.message || `OpenRouter API error: ${response.statusText}`,
125
+ code: `OPENROUTER_${response.status}_ERROR`
126
+ }
127
+ };
128
+ } catch (parseError) {
129
+ // 如果无法解析OpenRouter的错误JSON,使用通用错误格式
130
+ claudeErrorResponse = {
131
+ type: "error",
132
+ error: {
133
+ type: "api_error",
134
+ message: `OpenRouter API error: ${response.statusText}`,
135
+ code: `OPENROUTER_${response.status}_ERROR`
136
+ }
137
+ };
138
+ }
139
+
140
+ // 返回转换后的错误响应
141
+ return new Response(JSON.stringify(claudeErrorResponse), {
142
+ status: response.status,
143
+ statusText: response.statusText,
144
+ headers: {
145
+ "Content-Type": "application/json"
146
+ }
147
+ });
148
+ }
149
+
150
+ //完整的请求日志,保护请求和响应
151
+ let fullLog = {request:{
152
+ url:url,
153
+ method: init.method,
154
+ headers: headersToObject(init.headers),
155
+ body: initBody
156
+ },response:{
157
+ status: response.status,
158
+ statusText: response.statusText,
159
+ headers: headersToObject(response.headers)
160
+ },openai:{
161
+ request: {
162
+ body: requestBody
163
+ },
164
+ response: {}
165
+ }};
166
+
167
+ let res = await anthropicTransformer.transformResponseIn(responseToClient);
168
+ let toClientRes = await res.clone();
169
+
170
+ (async () => {
171
+
172
+ fullLog.openai.response.body = await parseOpenAIChatCompletion(await response.text());
173
+ fullLog.response.body = mergeAnthropic(await res.text());
174
+
175
+ //其他类型是错误的
176
+ logAPI(fullLog);
177
+
178
+ })().catch(err => console.error('日志解析错误:', err));
179
+
180
+ return toClientRes;
181
+
182
+
183
+ };
184
+ global.fetch.__ProxyInstrumented = true;
185
+ }
186
+ try{
187
+ instrumentFetch();
188
+ }catch(e){
189
+ logger.system.error(e);
190
+ }