@oevortex/opencode-qwen-auth 0.1.0 → 0.1.2

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.
@@ -1,190 +0,0 @@
1
- /**
2
- * Thinking Block Parser for Qwen
3
- * Handles <think>...</think> tags in the stream to separate
4
- * reasoning content from regular text content.
5
- *
6
- * Based on revibe/core/llm/backend/qwen/handler.py
7
- */
8
-
9
- export interface ParseResult {
10
- /** Regular content (outside thinking blocks) */
11
- regularContent: string;
12
- /** Reasoning/thinking content (inside thinking blocks) */
13
- reasoningContent: string;
14
- }
15
-
16
- /**
17
- * Parser for Qwen's thinking/reasoning blocks.
18
- *
19
- * Handles `<think>...</think>` tags in the stream to separate
20
- * reasoning content from regular text content.
21
- *
22
- * This parser is stateful and designed for streaming scenarios
23
- * where tags might be split across multiple chunks.
24
- */
25
- export class ThinkingBlockParser {
26
- private inThinkingBlock = false;
27
- private buffer = "";
28
-
29
- /**
30
- * Reset the parser state.
31
- * Call this when starting a new message/response.
32
- */
33
- reset(): void {
34
- this.inThinkingBlock = false;
35
- this.buffer = "";
36
- }
37
-
38
- /**
39
- * Check if currently inside a thinking block.
40
- */
41
- isInThinkingBlock(): boolean {
42
- return this.inThinkingBlock;
43
- }
44
-
45
- /**
46
- * Parse text and separate thinking content from regular content.
47
- *
48
- * This method is designed for streaming - call it with each chunk
49
- * of text as it arrives, and it will return the parsed content.
50
- *
51
- * @param text - The text chunk to parse
52
- * @returns Object containing regular and reasoning content
53
- */
54
- parse(text: string): ParseResult {
55
- let regularContent = "";
56
- let reasoningContent = "";
57
-
58
- // Add to buffer and process
59
- this.buffer += text;
60
-
61
- while (true) {
62
- if (this.inThinkingBlock) {
63
- // Look for closing tag
64
- const endIdx = this.buffer.indexOf("</think>");
65
- if (endIdx !== -1) {
66
- reasoningContent += this.buffer.slice(0, endIdx);
67
- this.buffer = this.buffer.slice(endIdx + 8); // len("</think>") = 8
68
- this.inThinkingBlock = false;
69
- } else {
70
- // Still in thinking block, emit all as reasoning
71
- reasoningContent += this.buffer;
72
- this.buffer = "";
73
- break;
74
- }
75
- } else {
76
- // Look for opening tag
77
- const startIdx = this.buffer.indexOf("<think>");
78
- if (startIdx !== -1) {
79
- regularContent += this.buffer.slice(0, startIdx);
80
- this.buffer = this.buffer.slice(startIdx + 7); // len("<think>") = 7
81
- this.inThinkingBlock = true;
82
- } else {
83
- // Check for partial opening tag at the end of buffer
84
- // This handles cases where "<think>" is split across chunks
85
- const partialTag = this.findPartialOpenTag();
86
- if (partialTag > 0) {
87
- // Keep potential partial tag in buffer, emit the rest
88
- regularContent += this.buffer.slice(0, -partialTag);
89
- this.buffer = this.buffer.slice(-partialTag);
90
- } else {
91
- // No thinking block, emit all as regular content
92
- regularContent += this.buffer;
93
- this.buffer = "";
94
- }
95
- break;
96
- }
97
- }
98
- }
99
-
100
- return { regularContent, reasoningContent };
101
- }
102
-
103
- /**
104
- * Find if there's a partial opening tag at the end of the buffer.
105
- * Returns the length of the partial tag, or 0 if none found.
106
- */
107
- private findPartialOpenTag(): number {
108
- const tag = "<think>";
109
- for (let len = Math.min(tag.length - 1, this.buffer.length); len > 0; len--) {
110
- const suffix = this.buffer.slice(-len);
111
- if (tag.startsWith(suffix)) {
112
- return len;
113
- }
114
- }
115
- return 0;
116
- }
117
-
118
- /**
119
- * Flush any remaining content in the buffer.
120
- * Call this when the stream is complete.
121
- *
122
- * @returns Any remaining content based on current state
123
- */
124
- flush(): ParseResult {
125
- const result: ParseResult = {
126
- regularContent: "",
127
- reasoningContent: "",
128
- };
129
-
130
- if (this.buffer.length > 0) {
131
- if (this.inThinkingBlock) {
132
- result.reasoningContent = this.buffer;
133
- } else {
134
- result.regularContent = this.buffer;
135
- }
136
- this.buffer = "";
137
- }
138
-
139
- return result;
140
- }
141
- }
142
-
143
- /**
144
- * Parse complete text for thinking blocks (non-streaming).
145
- *
146
- * Use this for non-streaming responses where the entire text is available.
147
- *
148
- * @param text - The complete text to parse
149
- * @returns Object containing regular and reasoning content
150
- */
151
- export function parseThinkingBlocks(text: string): ParseResult {
152
- const parser = new ThinkingBlockParser();
153
- const result = parser.parse(text);
154
- const flushed = parser.flush();
155
-
156
- return {
157
- regularContent: result.regularContent + flushed.regularContent,
158
- reasoningContent: result.reasoningContent + flushed.reasoningContent,
159
- };
160
- }
161
-
162
- /**
163
- * Remove thinking blocks from text, returning only regular content.
164
- *
165
- * @param text - The text to process
166
- * @returns Text with thinking blocks removed
167
- */
168
- export function stripThinkingBlocks(text: string): string {
169
- return parseThinkingBlocks(text).regularContent;
170
- }
171
-
172
- /**
173
- * Extract only thinking/reasoning content from text.
174
- *
175
- * @param text - The text to process
176
- * @returns Only the content inside thinking blocks
177
- */
178
- export function extractThinkingContent(text: string): string {
179
- return parseThinkingBlocks(text).reasoningContent;
180
- }
181
-
182
- /**
183
- * Check if text contains any thinking blocks.
184
- *
185
- * @param text - The text to check
186
- * @returns True if text contains <think> tags
187
- */
188
- export function hasThinkingBlocks(text: string): boolean {
189
- return text.includes("<think>") || text.includes("</think>");
190
- }
package/src/types.ts DELETED
@@ -1,292 +0,0 @@
1
- /**
2
- * Qwen OAuth and plugin types
3
- * Based on revibe/core/llm/backend/qwen implementation
4
- */
5
-
6
- // ============================================================
7
- // OAuth Types
8
- // ============================================================
9
-
10
- export interface QwenOAuthCredentials {
11
- access_token: string;
12
- refresh_token: string;
13
- token_type: string;
14
- expiry_date: number; // Timestamp in milliseconds
15
- resource_url?: string;
16
- }
17
-
18
- export interface QwenTokenResponse {
19
- access_token: string;
20
- refresh_token?: string;
21
- token_type: string;
22
- expires_in: number; // Seconds until expiration
23
- error?: string;
24
- error_description?: string;
25
- }
26
-
27
- // ============================================================
28
- // Model Types
29
- // ============================================================
30
-
31
- export interface QwenModelInfo {
32
- id: string;
33
- name: string;
34
- context_window?: number;
35
- max_output?: number;
36
- input_price?: number; // Per million tokens
37
- output_price?: number; // Per million tokens
38
- supports_native_tools?: boolean;
39
- supports_thinking?: boolean;
40
- }
41
-
42
- // ============================================================
43
- // Stream Chunk Types
44
- // ============================================================
45
-
46
- export type StreamChunkType =
47
- | "text"
48
- | "reasoning"
49
- | "thinking_complete"
50
- | "usage"
51
- | "tool_call"
52
- | "tool_call_start"
53
- | "tool_call_delta"
54
- | "tool_call_end"
55
- | "tool_call_partial"
56
- | "error";
57
-
58
- export interface StreamTextChunk {
59
- type: "text";
60
- text: string;
61
- }
62
-
63
- export interface StreamReasoningChunk {
64
- type: "reasoning";
65
- text: string;
66
- signature?: string;
67
- }
68
-
69
- export interface StreamUsageChunk {
70
- type: "usage";
71
- input_tokens: number;
72
- output_tokens: number;
73
- cache_write_tokens?: number;
74
- cache_read_tokens?: number;
75
- reasoning_tokens?: number;
76
- }
77
-
78
- export interface StreamToolCallPartialChunk {
79
- type: "tool_call_partial";
80
- index: number;
81
- id?: string;
82
- name?: string;
83
- arguments?: string;
84
- }
85
-
86
- export interface StreamErrorChunk {
87
- type: "error";
88
- error: string;
89
- message: string;
90
- }
91
-
92
- export type StreamChunk =
93
- | StreamTextChunk
94
- | StreamReasoningChunk
95
- | StreamUsageChunk
96
- | StreamToolCallPartialChunk
97
- | StreamErrorChunk;
98
-
99
- // ============================================================
100
- // API Request/Response Types
101
- // ============================================================
102
-
103
- export interface ChatMessage {
104
- role: "system" | "user" | "assistant" | "tool";
105
- content?: string;
106
- reasoning_content?: string;
107
- tool_calls?: ToolCall[];
108
- tool_call_id?: string;
109
- name?: string;
110
- }
111
-
112
- export interface ToolCall {
113
- id?: string;
114
- index?: number;
115
- type?: "function";
116
- function: FunctionCall;
117
- }
118
-
119
- export interface FunctionCall {
120
- name?: string;
121
- arguments?: string;
122
- }
123
-
124
- export interface Tool {
125
- type: "function";
126
- function: {
127
- name: string;
128
- description?: string;
129
- parameters?: Record<string, unknown>;
130
- };
131
- }
132
-
133
- export type ToolChoice = "none" | "auto" | "required" | { type: "function"; function: { name: string } };
134
-
135
- export interface ChatCompletionRequest {
136
- model: string;
137
- messages: ChatMessage[];
138
- temperature?: number;
139
- max_tokens?: number;
140
- tools?: Tool[];
141
- tool_choice?: ToolChoice;
142
- stream?: boolean;
143
- stream_options?: {
144
- include_usage?: boolean;
145
- };
146
- }
147
-
148
- export interface ChatCompletionChoice {
149
- index: number;
150
- message?: ChatMessage;
151
- delta?: Partial<ChatMessage>;
152
- finish_reason?: string | null;
153
- }
154
-
155
- export interface ChatCompletionUsage {
156
- prompt_tokens: number;
157
- completion_tokens: number;
158
- total_tokens?: number;
159
- }
160
-
161
- export interface ChatCompletionResponse {
162
- id: string;
163
- object: string;
164
- created: number;
165
- model: string;
166
- choices: ChatCompletionChoice[];
167
- usage?: ChatCompletionUsage;
168
- error?: {
169
- message: string;
170
- type?: string;
171
- code?: string;
172
- };
173
- }
174
-
175
- export interface ChatCompletionChunk {
176
- id: string;
177
- object: string;
178
- created: number;
179
- model: string;
180
- choices: ChatCompletionChoice[];
181
- usage?: ChatCompletionUsage;
182
- error?: {
183
- message: string;
184
- type?: string;
185
- code?: string;
186
- };
187
- }
188
-
189
- // ============================================================
190
- // Plugin Types
191
- // ============================================================
192
-
193
- export interface AuthDetails {
194
- type: "oauth" | "api";
195
- }
196
-
197
- export interface OAuthAuthDetails extends AuthDetails {
198
- type: "oauth";
199
- access?: string;
200
- refresh: string;
201
- expires?: number;
202
- }
203
-
204
- export interface ApiAuthDetails extends AuthDetails {
205
- type: "api";
206
- apiKey: string;
207
- }
208
-
209
- export interface RefreshParts {
210
- refreshToken: string;
211
- resourceUrl?: string;
212
- }
213
-
214
- export interface LoaderResult {
215
- apiKey?: string;
216
- baseUrl?: string;
217
- fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
218
- }
219
-
220
- export interface Provider {
221
- models?: Record<string, { cost?: { input: number; output: number } } | undefined>;
222
- }
223
-
224
- export type GetAuth = () => Promise<AuthDetails>;
225
-
226
- export interface PluginContext {
227
- client: {
228
- auth: {
229
- set: (options: { path: { id: string }; body: Record<string, unknown> }) => Promise<void>;
230
- };
231
- tui: {
232
- showToast: (options: { body: { message: string; variant: "info" | "success" | "warning" | "error" } }) => Promise<void>;
233
- };
234
- log?: {
235
- debug: (message: string, meta?: Record<string, unknown>) => void;
236
- info: (message: string, meta?: Record<string, unknown>) => void;
237
- warn: (message: string, meta?: Record<string, unknown>) => void;
238
- error: (message: string, meta?: Record<string, unknown>) => void;
239
- };
240
- };
241
- }
242
-
243
- export interface AuthorizeResult {
244
- url: string;
245
- instructions?: string;
246
- method: "auto" | "manual";
247
- callback: () => Promise<TokenExchangeResult>;
248
- }
249
-
250
- export interface TokenExchangeResult {
251
- type: "success" | "failed";
252
- refresh?: string;
253
- access?: string;
254
- expires?: number;
255
- error?: string;
256
- resourceUrl?: string;
257
- }
258
-
259
- export interface AuthMethod {
260
- label: string;
261
- type: "oauth" | "api";
262
- authorize?: () => Promise<AuthorizeResult>;
263
- prompts?: Array<{
264
- type: "text" | "password";
265
- message: string;
266
- key: string;
267
- }>;
268
- }
269
-
270
- export interface PluginResult {
271
- auth?: {
272
- provider: string;
273
- loader: (getAuth: GetAuth, provider: Provider) => Promise<LoaderResult | Record<string, unknown>>;
274
- methods: AuthMethod[];
275
- };
276
- tool?: Record<string, unknown>;
277
- }
278
-
279
- // ============================================================
280
- // Rate Limit Types
281
- // ============================================================
282
-
283
- export interface RateLimitState {
284
- consecutive429: number;
285
- lastAt: number;
286
- }
287
-
288
- export interface RateLimitDelay {
289
- attempt: number;
290
- serverRetryAfterMs: number | null;
291
- delayMs: number;
292
- }