@anionex/dsh-vision-toolkit 0.1.5 → 0.1.6

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/src/config.ts CHANGED
@@ -15,15 +15,24 @@ import { VisionToolkitError } from './errors.ts'
15
15
  /** Settings document namespace owned by this plugin. */
16
16
  export const VISION_TOOLKIT_SETTINGS_NAMESPACE = settingsNamespace('vision-toolkit')
17
17
 
18
+ /** Browser-compatible default shared with the vendored Python client. */
19
+ export const DEFAULT_VISION_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'
20
+
18
21
  /** Full user-facing configuration; every field defaults at the schema boundary. */
19
22
  export interface VisionToolkitConfig {
20
23
  provider?: {
21
- /** OpenAI-compatible chat/completions base URL. */
24
+ /** Provider API base URL. */
22
25
  baseUrl?: string
23
26
  /** DSH Credential reference holding the API key (an environment-style name). */
24
27
  credential?: string
25
28
  /** Multimodal model name. */
26
29
  model?: string
30
+ /** Vision request protocol: OpenAI Chat Completions or Anthropic Messages. */
31
+ protocol?: 'openai' | 'anthropic'
32
+ /** Anthropic thinking field behavior; `omit` leaves model defaults untouched. */
33
+ anthropicThinking?: 'omit' | 'disabled' | 'adaptive'
34
+ /** Outbound User-Agent for provider requests and connection tests. */
35
+ userAgent?: string
27
36
  }
28
37
  /** Vision output language (`zh` or `en`). */
29
38
  language?: 'zh' | 'en'
@@ -53,6 +62,9 @@ export const Config: Schema<VisionToolkitConfig> = z.object({
53
62
  baseUrl: z.string().default('https://api.inferera.com/v1'),
54
63
  credential: z.string().default('VISION_API_KEY'),
55
64
  model: z.string().default('gemini-3.6-flash'),
65
+ protocol: z.union(['openai', 'anthropic'] as const).default('openai'),
66
+ anthropicThinking: z.union(['omit', 'disabled', 'adaptive'] as const).default('omit'),
67
+ userAgent: z.string().default(DEFAULT_VISION_USER_AGENT),
56
68
  }),
57
69
  language: z.union(['zh', 'en'] as const).default('zh'),
58
70
  timeoutMs: z.number().default(60000),
@@ -73,6 +85,9 @@ export interface ResolvedVisionToolkitConfig {
73
85
  baseUrl: string
74
86
  credential: CredentialRef
75
87
  model: string
88
+ protocol: 'openai' | 'anthropic'
89
+ anthropicThinking: 'omit' | 'disabled' | 'adaptive'
90
+ userAgent: string
76
91
  }
77
92
  language: 'zh' | 'en'
78
93
  timeoutMs: number
@@ -121,6 +136,18 @@ export function resolveConfig(config: VisionToolkitConfig = {}): ResolvedVisionT
121
136
  if (model.length === 0) {
122
137
  throw new VisionToolkitError('config', 'provider.model must not be empty')
123
138
  }
139
+ const protocol = provider.protocol ?? 'openai'
140
+ if (protocol !== 'openai' && protocol !== 'anthropic') {
141
+ throw new VisionToolkitError('config', 'provider.protocol must be "openai" or "anthropic"')
142
+ }
143
+ const anthropicThinking = provider.anthropicThinking ?? 'omit'
144
+ if (anthropicThinking !== 'omit' && anthropicThinking !== 'disabled' && anthropicThinking !== 'adaptive') {
145
+ throw new VisionToolkitError('config', 'provider.anthropicThinking must be "omit", "disabled", or "adaptive"')
146
+ }
147
+ const userAgent = (provider.userAgent ?? DEFAULT_VISION_USER_AGENT).trim()
148
+ if (userAgent.length === 0) {
149
+ throw new VisionToolkitError('config', 'provider.userAgent must not be empty')
150
+ }
124
151
  const language = config.language ?? 'zh'
125
152
  if (language !== 'zh' && language !== 'en') {
126
153
  throw new VisionToolkitError('config', 'language must be "zh" or "en"')
@@ -161,7 +188,7 @@ export function resolveConfig(config: VisionToolkitConfig = {}): ResolvedVisionT
161
188
  }
162
189
  const allowedDirs = (config.allowedDirs ?? []).map(dir => dir.trim()).filter(dir => dir.length > 0)
163
190
  return {
164
- provider: { baseUrl, credential, model },
191
+ provider: { baseUrl, credential, model, protocol, anthropicThinking, userAgent },
165
192
  language,
166
193
  timeoutMs,
167
194
  maxImageBytes,
package/src/runtime.ts CHANGED
@@ -768,6 +768,9 @@ export class VisionToolkitRuntime {
768
768
  VISION_API_KEY: resolved.value,
769
769
  VISION_BASE_URL: this.config.provider.baseUrl,
770
770
  VISION_MODEL: this.config.provider.model,
771
+ VISION_API_PROTOCOL: this.config.provider.protocol === 'anthropic' ? 'anthropic' : 'chat_completions',
772
+ VISION_ANTHROPIC_THINKING: this.config.provider.anthropicThinking,
773
+ VISION_USER_AGENT: this.config.provider.userAgent,
771
774
  LANG: this.config.language,
772
775
  }
773
776
  }
@@ -832,6 +835,9 @@ export class VisionToolkitRuntime {
832
835
  provider: {
833
836
  baseUrl: env.VISION_BASE_URL,
834
837
  model: env.VISION_MODEL,
838
+ protocol: env.VISION_API_PROTOCOL,
839
+ anthropicThinking: env.VISION_ANTHROPIC_THINKING,
840
+ userAgent: env.VISION_USER_AGENT,
835
841
  language: env.LANG,
836
842
  credentialSha256: createHash('sha256').update(env.VISION_API_KEY).digest('hex'),
837
843
  },
@@ -1735,9 +1741,19 @@ export class VisionToolkitRuntime {
1735
1741
  const endpoint = `${this.config.provider.baseUrl}/models`
1736
1742
  try {
1737
1743
  const started = Date.now()
1744
+ const headers: Record<string, string> = {
1745
+ Accept: 'application/json',
1746
+ 'User-Agent': this.config.provider.userAgent,
1747
+ }
1748
+ if (this.config.provider.protocol === 'anthropic') {
1749
+ headers['x-api-key'] = resolvedCredential.value
1750
+ headers['anthropic-version'] = '2023-06-01'
1751
+ } else {
1752
+ headers.Authorization = `Bearer ${resolvedCredential.value}`
1753
+ }
1738
1754
  const response = await fetch(endpoint, {
1739
1755
  method: 'GET',
1740
- headers: { Authorization: `Bearer ${resolvedCredential.value}`, Accept: 'application/json' },
1756
+ headers,
1741
1757
  signal: operation.signal,
1742
1758
  })
1743
1759
  operation.metrics.upstreamMs += Date.now() - started
package/src/upstream.ts CHANGED
@@ -38,6 +38,9 @@ export interface UpstreamEnvironment {
38
38
  VISION_API_KEY: string
39
39
  VISION_BASE_URL: string
40
40
  VISION_MODEL: string
41
+ VISION_API_PROTOCOL: 'chat_completions' | 'anthropic'
42
+ VISION_ANTHROPIC_THINKING: 'omit' | 'disabled' | 'adaptive'
43
+ VISION_USER_AGENT: string
41
44
  LANG: 'zh' | 'en'
42
45
  }
43
46
 
@@ -612,6 +615,9 @@ export class UpstreamAdapter {
612
615
  VISION_API_KEY: options.env.VISION_API_KEY,
613
616
  VISION_BASE_URL: options.env.VISION_BASE_URL,
614
617
  VISION_MODEL: options.env.VISION_MODEL,
618
+ VISION_API_PROTOCOL: options.env.VISION_API_PROTOCOL,
619
+ VISION_ANTHROPIC_THINKING: options.env.VISION_ANTHROPIC_THINKING,
620
+ VISION_USER_AGENT: options.env.VISION_USER_AGENT,
615
621
  LANG: options.env.LANG,
616
622
  VISION_ENV_FILE: join(prepared.cleanHome, 'vision.env'),
617
623
  }),
package/src/web.ts CHANGED
@@ -289,7 +289,7 @@ export class VisionToolkitWebBackend {
289
289
  }
290
290
 
291
291
  /**
292
- * Attach optional Web routes whenever an webServer service is present.
292
+ * Attach optional Web routes whenever a webServer service is present.
293
293
  * @param ctx - plugin context owning route effects.
294
294
  * @param backend - Settings handler.
295
295
  * @param artifacts - signed Artifact handler.
@@ -2,6 +2,19 @@
2
2
 
3
3
  All notable user-facing changes to agent-vision-toolkit are documented in this file.
4
4
 
5
+ ## [Unreleased]
6
+
7
+ ### Added
8
+
9
+ - Let the shared Python vision client call either Chat Completions or Responses APIs, including optional reasoning effort and explicit `store: false` data handling.
10
+ - Add native Anthropic Messages requests with protocol-specific authentication, image sources, optional thinking control, and text-block response extraction.
11
+ - Rewrite OpenAI Chat Completions `image_url` blocks through the existing vision-description pipeline with a host-neutral channel note.
12
+
13
+ ### Fixed
14
+
15
+ - Send a browser-compatible, configurable User-Agent from the shared Python vision client so Cloudflare-backed OpenAI-compatible endpoints do not reject the default `Python-urllib` signature.
16
+ - Honor `Retry-After` and retry Anthropic 529 overload responses.
17
+
5
18
  ## [0.1.0] - 2026-08-07
6
19
 
7
20
  ### Added
@@ -6,6 +6,7 @@
6
6
 
7
7
  # agent-vision-toolkit
8
8
 
9
+ [![X (Twitter)](https://img.shields.io/badge/-@anion__ex-000000?style=flat-square&logo=x&logoColor=white)](https://x.com/anion_ex)
9
10
  [![GitHub stars](https://img.shields.io/github/stars/Anionex/agent-vision-toolkit?style=flat-square&logo=github)](https://github.com/Anionex/agent-vision-toolkit/stargazers)
10
11
  [![GitHub forks](https://img.shields.io/github/forks/Anionex/agent-vision-toolkit?style=flat-square&logo=github)](https://github.com/Anionex/agent-vision-toolkit/forks)
11
12
  [![License: MIT](https://img.shields.io/github/license/Anionex/agent-vision-toolkit?style=flat-square&color=4EAA25)](https://github.com/Anionex/agent-vision-toolkit/blob/main/LICENSE)
@@ -136,7 +137,7 @@ When to use them, the order in which to call tools, and how to verify the result
136
137
 
137
138
  > Read https://github.com/Anionex/agent-vision-toolkit/blob/main/AGENT_INSTALL.md in full, then install the appropriate vision proxy or native extension/plugin for the agent application we are currently using. If the vision API is not configured, locate the configuration file for the current operating system and guide me through setting `VISION_API_KEY`, `VISION_BASE_URL`, and `VISION_MODEL`.
138
139
 
139
- All you need to prepare is an OpenAI-compatible multimodal API base URL, API key, and model name. The agent will guide you through writing them to the appropriate configuration file.
140
+ All you need to prepare is a multimodal API supporting OpenAI Chat Completions, OpenAI Responses, or Anthropic Messages, plus its base URL, API key, and model name. The agent will guide you through writing them to the appropriate configuration file.
140
141
 
141
142
  > After installing the optional integration and restarting the agent, paste an image directly or let the model call its built-in image tool. Pi, Oh My Pi, and OpenCode use single-file [native extensions](extensions/) rather than the proxy; see each agent's documentation.
142
143
 
@@ -151,7 +152,7 @@ VISION_BASE_URL=https://openrouter.ai/api/v1
151
152
  VISION_MODEL=google/gemini-3.6-flash
152
153
  ```
153
154
 
154
- Any OpenAI-compatible endpoint that supports `/chat/completions` with `image_url` works (e.g. Aliyun DashScope: `https://dashscope.aliyuncs.com/compatible-mode/v1` + `qwen-vl-max-latest`). Add `LANG=en` for English descriptions (default is Chinese).
155
+ Any OpenAI-compatible endpoint that supports `/chat/completions` with `image_url` works (e.g. Aliyun DashScope: `https://dashscope.aliyuncs.com/compatible-mode/v1` + `qwen-vl-max-latest`). The Python client/proxy can also use `/responses` with `input_image` by setting `VISION_API_PROTOCOL=responses`, or Anthropic Messages by setting `VISION_API_PROTOCOL=anthropic` and a base URL ending in `/v1` (not `/messages`). Add `LANG=en` for English descriptions (default is Chinese).
155
156
 
156
157
  **2. Put the CLIs on your PATH:**
157
158
 
@@ -322,14 +323,18 @@ Codex -> 127.0.0.1:19100 -> your existing text-only upstream
322
323
  <details>
323
324
  <summary><b>Environment variables</b></summary>
324
325
 
325
- The toolkit and proxy use only these environment variables; just three are required:
326
+ The standalone CLIs and Python proxy use these environment variables; just three are required. The native Pi and OpenCode extensions use their own settings and currently call `/chat/completions` only.
326
327
 
327
328
  | Variable | Required | Description |
328
329
  |---|---:|---|
329
330
  | `VISION_API_KEY` | Yes | API key of the multimodal model |
330
- | `VISION_BASE_URL` | Yes | OpenAI-compatible API base URL |
331
+ | `VISION_BASE_URL` | Yes | Provider API base URL; include `/v1` but not the protocol endpoint such as `/messages` |
331
332
  | `VISION_MODEL` | Yes | Multimodal model name |
332
333
  | `LANG` | No | Vision model output language: `zh` (Chinese) or `en` (English); default `zh` |
334
+ | `VISION_API_PROTOCOL` | No | Python client/proxy protocol: `chat_completions` (default), `responses`, or `anthropic`; Anthropic mode uses `x-api-key` and `anthropic-version` |
335
+ | `VISION_REASONING_EFFORT` | No | Optional provider-supported reasoning effort for the Python client/proxy when using `responses` |
336
+ | `VISION_ANTHROPIC_THINKING` | No | Anthropic thinking mode. `omit` (default) sends no thinking field and has the broadest compatibility. Use `disabled` or `adaptive` only when the selected model documents that mode; restore `omit` first if the provider returns HTTP 400. Manual `enabled` plus `budget_tokens` is not exposed. |
337
+ | `VISION_USER_AGENT` | No | Outbound User-Agent for the Python client/proxy; defaults to a browser-compatible value and can be overridden for provider requirements |
333
338
 
334
339
  </details>
335
340
 
@@ -348,7 +353,7 @@ The route whose connection (TCP/TLS handshake) succeeds is kept in memory and re
348
353
  ## Prerequisites
349
354
 
350
355
  - A coding agent already working with a model, including a text-only model such as DeepSeek V4
351
- - An OpenAI-compatible vision API that supports `/chat/completions` and `image_url`
356
+ - A vision API supporting OpenAI Chat Completions, OpenAI Responses, or Anthropic Messages; select the latter two with `VISION_API_PROTOCOL=responses` or `VISION_API_PROTOCOL=anthropic`
352
357
  - No other configuration is required
353
358
 
354
359
  ## FAQ
@@ -396,4 +401,4 @@ To reduce costs further, you can use a locally deployed small multimodal side mo
396
401
 
397
402
  If agent-vision-toolkit saves you time, you are welcome to star it, share it, contribute, or [sponsor the project](FUNDING.md).
398
403
 
399
- I'm [anionex](https://anionex.me/), an AI-native developer who once ranked No. 4 on GitHub's global developer trending list, with more than 16k stars across my projects. If you would like to follow my future work, [follow me on GitHub](https://github.com/Anionex).
404
+ I'm [anionex](https://anionex.me/), an AI-native developer who once ranked No. 4 on GitHub's global developer trending list, with more than 16k stars across my projects. If you would like to follow my future work, [follow me on X](https://x.com/anion_ex) or [GitHub](https://github.com/Anionex).
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "repository": "https://github.com/Anionex/agent-vision-toolkit",
4
- "version": "v0.1.0+snapshot.8ccf9d7",
5
- "commit": "8ccf9d7d89bc395ad7dd19dde840401a5b84623d",
6
- "contentSha256": "99af16244fef18745ab0842ff555a54d1829d4ed588adfa7e05756beffe813c2",
4
+ "version": "v0.1.0+snapshot.bc9803d",
5
+ "commit": "bc9803d7d6300c864d17460ecbb33540b26638e0",
6
+ "contentSha256": "6936f8fba3ed40f54267245e717603c2622649ce4bd7ccf3c1b23f39047b1f3f",
7
7
  "files": [
8
8
  {
9
9
  "path": "CHANGELOG.md",
10
- "bytes": 913,
11
- "sha256": "3eb86d51d5fd89714045e73a29ebd4aa5b146e9bf5f8c8620cc9b452a8d4bbe0"
10
+ "bytes": 1671,
11
+ "sha256": "0bb23408797c8e5389abc0b34d47b43907316f8bd106420e4c05f25606bccde1"
12
12
  },
13
13
  {
14
14
  "path": "LICENSE",
@@ -17,8 +17,8 @@
17
17
  },
18
18
  {
19
19
  "path": "README.md",
20
- "bytes": 22087,
21
- "sha256": "c530e6d123a2a535742ddf1637ca78150b8f96cd2488d5763fc0220ba75aab05"
20
+ "bytes": 23625,
21
+ "sha256": "55d880175b51d288f5b817d064ebb79e6c8663ee0bb1817f4a0390ab18b5c3ee"
22
22
  },
23
23
  {
24
24
  "path": "bin/crop",
@@ -80,10 +80,15 @@
80
80
  "bytes": 3978,
81
81
  "sha256": "70e04fea9bf452f35d12bed0c19c7e2be46934a5423caa778b24da82b78f2251"
82
82
  },
83
+ {
84
+ "path": "tests/test_vision_client.py",
85
+ "bytes": 17933,
86
+ "sha256": "eb31118751a5724385f486b98d45af83cad1cf7e4506b79ddf9a840d08f22367"
87
+ },
83
88
  {
84
89
  "path": "vision_client.py",
85
- "bytes": 6117,
86
- "sha256": "5450985379a0c1e6460594a8739966fe1e2489311ce473b1d92021ccf41d0be8"
90
+ "bytes": 10610,
91
+ "sha256": "7a899d08b1c65721c9fdfd42d9d3288671c149fdc919b5dedb5b36cc6b06ddce"
87
92
  }
88
93
  ]
89
94
  }