@serii84/vertex-partner-provider 1.0.8 → 1.0.10

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 +64 -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.10 - Skip reasoning_content, only pass actual content
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,71 @@ 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'));
52
- continue;
53
- }
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()) continue;
54
46
 
55
- try {
56
- const parsed = JSON.parse(data);
47
+ if (line.startsWith('data: ')) {
48
+ const data = line.slice(6).trim();
49
+
50
+ if (data === '[DONE]') {
51
+ controller.enqueue(encoder.encode('data: [DONE]\n\n'));
52
+ continue;
53
+ }
57
54
 
58
- // Transform: move reasoning_content to content if content is null
59
- if (parsed.choices) {
55
+ try {
56
+ const parsed = JSON.parse(data);
57
+
58
+ // Skip empty choices
59
+ if (!parsed.choices || parsed.choices.length === 0) {
60
+ continue;
61
+ }
62
+
63
+ let hasContent = false;
64
+
60
65
  for (const choice of parsed.choices) {
61
66
  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;
67
+ // Only keep chunks that have actual content (not reasoning)
68
+ if (choice.delta.content && choice.delta.content !== null) {
69
+ hasContent = true;
65
70
  }
66
- // Remove reasoning_content to avoid confusion
71
+ // Remove reasoning_content entirely
67
72
  delete choice.delta.reasoning_content;
73
+
74
+ // If only reasoning was present, set empty content
75
+ if (!choice.delta.content) {
76
+ choice.delta.content = '';
77
+ }
68
78
  }
79
+
80
+ // Pass through finish_reason chunks
81
+ if (choice.finish_reason) {
82
+ hasContent = true;
83
+ }
84
+ }
85
+
86
+ // Only emit if there's actual content or it's a finish chunk
87
+ if (hasContent || parsed.choices.some(c => c.finish_reason)) {
88
+ controller.enqueue(encoder.encode('data: ' + JSON.stringify(parsed) + '\n\n'));
69
89
  }
90
+ } catch (e) {
91
+ controller.enqueue(encoder.encode(line + '\n'));
70
92
  }
71
-
72
- controller.enqueue(encoder.encode('data: ' + JSON.stringify(parsed) + '\n'));
73
- } catch (e) {
74
- // If JSON parse fails, pass through unchanged
75
- controller.enqueue(encoder.encode(line + '\n'));
76
93
  }
77
- } else if (line.trim()) {
78
- controller.enqueue(encoder.encode(line + '\n'));
79
94
  }
95
+ } catch (err) {
96
+ controller.error(err);
80
97
  }
81
98
  }
82
99
  });
@@ -110,11 +127,17 @@ function createVertexPartner(options = {}) {
110
127
  const headers = new Headers(init?.headers);
111
128
  headers.set('Authorization', `Bearer ${token}`);
112
129
 
130
+ let isStreaming = false;
131
+ if (init?.body) {
132
+ try {
133
+ const body = JSON.parse(init.body);
134
+ isStreaming = body.stream === true;
135
+ } catch (e) {}
136
+ }
137
+
113
138
  const response = await fetch(url, { ...init, headers });
114
139
 
115
- // Check if this is a streaming request
116
- const contentType = response.headers.get('content-type') || '';
117
- if (contentType.includes('text/event-stream')) {
140
+ if (isStreaming && response.ok) {
118
141
  return transformStream(response);
119
142
  }
120
143
 
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.10",
4
4
  "description": "Vertex AI partner models (GLM, Kimi, DeepSeek) for OpenCode",
5
5
  "main": "index.js",
6
6
  "scripts": {