@serii84/vertex-partner-provider 1.0.8 → 1.0.11

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.
Files changed (2) hide show
  1. package/index.js +58 -41
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Vertex Partner Provider for OpenCode
3
- * Transforms streaming responses to handle reasoning_content
3
+ * v1.0.11 - Convert reasoning_content to content, emit all chunks
4
4
  */
5
5
 
6
6
  const { createOpenAICompatible } = require('@ai-sdk/openai-compatible');
@@ -20,10 +20,6 @@ async function getAuthToken(googleAuthOptions) {
20
20
  return token.token;
21
21
  }
22
22
 
23
- /**
24
- * Transform SSE stream to handle reasoning_content
25
- * Converts reasoning_content to content so SDK can parse it
26
- */
27
23
  function transformStream(response) {
28
24
  const reader = response.body.getReader();
29
25
  const decoder = new TextDecoder();
@@ -33,50 +29,65 @@ function transformStream(response) {
33
29
 
34
30
  const transformedStream = new ReadableStream({
35
31
  async pull(controller) {
36
- const { done, value } = await reader.read();
37
-
38
- if (done) {
39
- controller.close();
40
- return;
41
- }
42
-
43
- buffer += decoder.decode(value, { stream: true });
44
- const lines = buffer.split('\n');
45
- buffer = lines.pop() || ''; // Keep incomplete line in buffer
46
-
47
- for (const line of lines) {
48
- if (line.startsWith('data: ')) {
49
- const data = line.slice(6);
50
- if (data === '[DONE]') {
51
- controller.enqueue(encoder.encode(line + '\n'));
32
+ try {
33
+ const { done, value } = await reader.read();
34
+
35
+ if (done) {
36
+ controller.close();
37
+ return;
38
+ }
39
+
40
+ buffer += decoder.decode(value, { stream: true });
41
+ const lines = buffer.split('\n');
42
+ buffer = lines.pop() || '';
43
+
44
+ for (const line of lines) {
45
+ if (!line.trim()) {
46
+ controller.enqueue(encoder.encode('\n'));
52
47
  continue;
53
48
  }
54
49
 
55
- try {
56
- const parsed = JSON.parse(data);
50
+ if (line.startsWith('data: ')) {
51
+ const data = line.slice(6).trim();
57
52
 
58
- // Transform: move reasoning_content to content if content is null
59
- if (parsed.choices) {
60
- for (const choice of parsed.choices) {
61
- if (choice.delta) {
62
- // If content is null but reasoning_content exists, use reasoning_content
63
- if (choice.delta.content === null && choice.delta.reasoning_content) {
64
- choice.delta.content = choice.delta.reasoning_content;
53
+ if (data === '[DONE]') {
54
+ controller.enqueue(encoder.encode('data: [DONE]\n\n'));
55
+ continue;
56
+ }
57
+
58
+ try {
59
+ const parsed = JSON.parse(data);
60
+
61
+ // Skip empty choices array (usage chunk at end)
62
+ if (parsed.choices && parsed.choices.length === 0) {
63
+ continue;
64
+ }
65
+
66
+ // Transform choices
67
+ if (parsed.choices) {
68
+ for (const choice of parsed.choices) {
69
+ if (choice.delta) {
70
+ // If content is null/empty but reasoning_content exists, use it
71
+ if (!choice.delta.content && choice.delta.reasoning_content) {
72
+ choice.delta.content = choice.delta.reasoning_content;
73
+ }
74
+ // Remove the non-standard field
75
+ delete choice.delta.reasoning_content;
65
76
  }
66
- // Remove reasoning_content to avoid confusion
67
- delete choice.delta.reasoning_content;
68
77
  }
69
78
  }
79
+
80
+ controller.enqueue(encoder.encode('data: ' + JSON.stringify(parsed) + '\n\n'));
81
+ } catch (e) {
82
+ // Pass through unparseable lines
83
+ controller.enqueue(encoder.encode(line + '\n'));
70
84
  }
71
-
72
- controller.enqueue(encoder.encode('data: ' + JSON.stringify(parsed) + '\n'));
73
- } catch (e) {
74
- // If JSON parse fails, pass through unchanged
85
+ } else {
75
86
  controller.enqueue(encoder.encode(line + '\n'));
76
87
  }
77
- } else if (line.trim()) {
78
- controller.enqueue(encoder.encode(line + '\n'));
79
88
  }
89
+ } catch (err) {
90
+ controller.error(err);
80
91
  }
81
92
  }
82
93
  });
@@ -110,11 +121,17 @@ function createVertexPartner(options = {}) {
110
121
  const headers = new Headers(init?.headers);
111
122
  headers.set('Authorization', `Bearer ${token}`);
112
123
 
124
+ let isStreaming = false;
125
+ if (init?.body) {
126
+ try {
127
+ const body = JSON.parse(init.body);
128
+ isStreaming = body.stream === true;
129
+ } catch (e) {}
130
+ }
131
+
113
132
  const response = await fetch(url, { ...init, headers });
114
133
 
115
- // Check if this is a streaming request
116
- const contentType = response.headers.get('content-type') || '';
117
- if (contentType.includes('text/event-stream')) {
134
+ if (isStreaming && response.ok) {
118
135
  return transformStream(response);
119
136
  }
120
137
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serii84/vertex-partner-provider",
3
- "version": "1.0.8",
3
+ "version": "1.0.11",
4
4
  "description": "Vertex AI partner models (GLM, Kimi, DeepSeek) for OpenCode",
5
5
  "main": "index.js",
6
6
  "scripts": {