dsh-browser-application 0.37.0
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/LICENSE +19 -0
- package/README.md +27 -0
- package/cordis.patch.yml +29 -0
- package/lib/index.js +3377 -0
- package/lib/invariant.js +26 -0
- package/lib/types/bridge-url.d.ts +27 -0
- package/lib/types/browser-context.d.ts +38 -0
- package/lib/types/dsh-gateway.d.ts +42 -0
- package/lib/types/event-generation.d.ts +56 -0
- package/lib/types/extension-sessions.d.ts +26 -0
- package/lib/types/host-api.d.ts +47 -0
- package/lib/types/image-relay.d.ts +43 -0
- package/lib/types/index.d.ts +158 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/remote-host-api.d.ts +12 -0
- package/lib/types/server.d.ts +166 -0
- package/lib/types/session-deferral.d.ts +33 -0
- package/lib/types/session-history.d.ts +30 -0
- package/lib/types/session-purge.d.ts +55 -0
- package/lib/types/session-workspace.d.ts +37 -0
- package/lib/types/token.d.ts +57 -0
- package/lib/types/tools.d.ts +42 -0
- package/lib/types/vision-selfcheck.d.ts +18 -0
- package/lib/types/vision.d.ts +57 -0
- package/package.json +95 -0
- package/src/bridge-url.ts +57 -0
- package/src/browser-context.ts +102 -0
- package/src/dsh-gateway.ts +66 -0
- package/src/event-generation.ts +385 -0
- package/src/extension-sessions.ts +40 -0
- package/src/host-api.ts +64 -0
- package/src/image-relay.ts +118 -0
- package/src/index.ts +575 -0
- package/src/invariant.ts +33 -0
- package/src/remote-host-api.ts +397 -0
- package/src/server.ts +658 -0
- package/src/session-deferral.ts +296 -0
- package/src/session-history.ts +220 -0
- package/src/session-purge.ts +154 -0
- package/src/session-workspace.ts +147 -0
- package/src/token.ts +100 -0
- package/src/tools.ts +301 -0
- package/src/vision-selfcheck.ts +35 -0
- package/src/vision.ts +135 -0
package/src/vision.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The multimodal call the desktop makes on the extension's behalf.
|
|
3
|
+
*
|
|
4
|
+
* Only the desktop can hold the model credential without putting it in a browser
|
|
5
|
+
* profile, and only the desktop can reach the network through the machine's own
|
|
6
|
+
* proxy, VPN, or client certificate. This module is the transport: the prompt,
|
|
7
|
+
* the request body, and the response parser are shared with the extension's own
|
|
8
|
+
* outbound path, so "which side calls the model" changes nothing about what is
|
|
9
|
+
* asked or what counts as an answer.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
buildVisionRequestBody,
|
|
16
|
+
parseVisionResponse,
|
|
17
|
+
reasoningTokensOf,
|
|
18
|
+
type VisionCallConfig,
|
|
19
|
+
type VisionCallContext,
|
|
20
|
+
type VisionCallImage,
|
|
21
|
+
type VisionCallResult,
|
|
22
|
+
} from '@dsh-browser/protocol'
|
|
23
|
+
|
|
24
|
+
export type { VisionCallContext, VisionCallResult }
|
|
25
|
+
|
|
26
|
+
/** A 1×1 transparent PNG: the smallest thing a vision endpoint will accept. */
|
|
27
|
+
export const PROBE_IMAGE_BASE64 =
|
|
28
|
+
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=='
|
|
29
|
+
|
|
30
|
+
/** What a probe learned about the deployment's cost controls. */
|
|
31
|
+
export interface VisionProbe {
|
|
32
|
+
ok: boolean
|
|
33
|
+
/** Reasoning tokens the response reported; 0 when none or unreported. */
|
|
34
|
+
reasoningTokens: number
|
|
35
|
+
message: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface VisionConfig extends VisionCallConfig {
|
|
39
|
+
/** Chat-completions base URL. */
|
|
40
|
+
baseUrl: string
|
|
41
|
+
apiKey: string
|
|
42
|
+
timeoutMs: number
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface VisionRequest extends VisionCallImage {
|
|
46
|
+
context: VisionCallContext
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type VisionResult = VisionCallResult
|
|
50
|
+
|
|
51
|
+
export class VisionClient {
|
|
52
|
+
private readonly config: VisionConfig
|
|
53
|
+
private readonly fetchImpl: typeof fetch
|
|
54
|
+
|
|
55
|
+
constructor(config: VisionConfig, fetchImpl: typeof fetch = fetch) {
|
|
56
|
+
this.config = config
|
|
57
|
+
this.fetchImpl = fetchImpl
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Describe one image.
|
|
62
|
+
*
|
|
63
|
+
* @param request - normalized bytes plus the page context around them.
|
|
64
|
+
* @returns one line of description, or a classified failure.
|
|
65
|
+
*/
|
|
66
|
+
async describe(request: VisionRequest): Promise<VisionResult> {
|
|
67
|
+
const endpoint = `${this.config.baseUrl.replace(/\/$/, '')}/chat/completions`
|
|
68
|
+
let response: Response
|
|
69
|
+
try {
|
|
70
|
+
response = await this.fetchImpl(endpoint, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: {
|
|
73
|
+
'content-type': 'application/json',
|
|
74
|
+
authorization: `Bearer ${this.config.apiKey}`,
|
|
75
|
+
},
|
|
76
|
+
body: JSON.stringify(buildVisionRequestBody(this.config, request, request.context)),
|
|
77
|
+
signal: AbortSignal.timeout(this.config.timeoutMs),
|
|
78
|
+
})
|
|
79
|
+
} catch (error: unknown) {
|
|
80
|
+
return { ok: false, code: 'vision-network', message: error instanceof Error ? error.message : String(error) }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!response.ok) {
|
|
84
|
+
const detail = await response.text().catch(() => '')
|
|
85
|
+
return { ok: false, code: 'vision-http', message: `${String(response.status)} ${detail.slice(0, 200)}` }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let payload: unknown
|
|
89
|
+
try {
|
|
90
|
+
payload = await response.json()
|
|
91
|
+
} catch (error: unknown) {
|
|
92
|
+
return { ok: false, code: 'vision-bad-response', message: error instanceof Error ? error.message : String(error) }
|
|
93
|
+
}
|
|
94
|
+
return parseVisionResponse(payload)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Send one minimal request and read the billing back.
|
|
99
|
+
*
|
|
100
|
+
* A thinking switch can be ignored silently — the request succeeds and the
|
|
101
|
+
* answer looks fine, while every image costs several times what it should. The
|
|
102
|
+
* only way to know is to look at `usage`, so this sends the smallest possible
|
|
103
|
+
* image once and reports what came back.
|
|
104
|
+
*
|
|
105
|
+
* @returns what the provider reported, or why the probe could not run.
|
|
106
|
+
*/
|
|
107
|
+
async probe(): Promise<VisionProbe> {
|
|
108
|
+
const endpoint = `${this.config.baseUrl.replace(/\/$/, '')}/chat/completions`
|
|
109
|
+
let response: Response
|
|
110
|
+
try {
|
|
111
|
+
response = await this.fetchImpl(endpoint, {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers: {
|
|
114
|
+
'content-type': 'application/json',
|
|
115
|
+
authorization: `Bearer ${this.config.apiKey}`,
|
|
116
|
+
},
|
|
117
|
+
body: JSON.stringify(buildVisionRequestBody(
|
|
118
|
+
this.config,
|
|
119
|
+
{ base64: PROBE_IMAGE_BASE64, mediaType: 'image/png' },
|
|
120
|
+
{ alt: '', near: '', heading: '', kind: 'icon' },
|
|
121
|
+
)),
|
|
122
|
+
signal: AbortSignal.timeout(this.config.timeoutMs),
|
|
123
|
+
})
|
|
124
|
+
} catch (error: unknown) {
|
|
125
|
+
return { ok: false, reasoningTokens: 0, message: error instanceof Error ? error.message : String(error) }
|
|
126
|
+
}
|
|
127
|
+
if (!response.ok) return { ok: false, reasoningTokens: 0, message: `HTTP ${String(response.status)}` }
|
|
128
|
+
try {
|
|
129
|
+
const payload = await response.json() as { usage?: Record<string, unknown> }
|
|
130
|
+
return { ok: true, reasoningTokens: reasoningTokensOf(payload.usage), message: 'ok' }
|
|
131
|
+
} catch (error: unknown) {
|
|
132
|
+
return { ok: false, reasoningTokens: 0, message: error instanceof Error ? error.message : String(error) }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|