@betterlu/agentlu 1.2.1 → 1.4.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/README.md +107 -1
- package/dist/index.d.ts +236 -18
- package/dist/index.js +179 -71
- package/package.json +14 -4
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ const client = new OpenAI({ baseURL: "http://localhost:8912/v1", apiKey });
|
|
|
10
10
|
const r = await client.responses.create({ model: "fast", input: "hi" });
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
This client adds get-or-create registration, typed `
|
|
13
|
+
This client adds get-or-create registration, typed `runJson`, and auto-retry on `409 thread_busy`.
|
|
14
14
|
|
|
15
15
|
## Install
|
|
16
16
|
|
|
@@ -33,4 +33,110 @@ const { data, response } = await alu.run({
|
|
|
33
33
|
console.log(response.output_text);
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
## JSON results
|
|
37
|
+
|
|
38
|
+
`runJson` returns only a completed response with the server-provided `output_parsed` value. It preserves JSON `null`, `false`, `0`, and empty arrays. A missing parsed value, failed response, incomplete response, cancellation, or queued response throws an SDK error instead of parsing `output_text`. `background: true` is rejected by this helper; use raw `run` when polling is intended.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const { data, response } = await alu.runJson<{ answer: string }>({
|
|
42
|
+
input: "Answer briefly",
|
|
43
|
+
schema: { type: "object", properties: { answer: { type: "string" } } },
|
|
44
|
+
schemaName: "Answer",
|
|
45
|
+
strict: true,
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
An explicit `text.format` is forwarded unchanged. In particular, `text: { format: { type: "text" } }` is not silently changed to JSON by `runJson`.
|
|
50
|
+
|
|
51
|
+
## Use-case defaults
|
|
52
|
+
|
|
53
|
+
Defaults are resolved by the service for every request:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
request field -> registered client default -> agent/profile default
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Register a client once per use-case. The runner sends only request-level overrides, so its registered schema is not resent with every run:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const schema = { type: "object", properties: { meal: { type: "string" } } };
|
|
63
|
+
const mealEnricher = alu.useCase("mealu-enrich", {
|
|
64
|
+
defaults: {
|
|
65
|
+
agent: "api",
|
|
66
|
+
store: false,
|
|
67
|
+
tools: [],
|
|
68
|
+
reasoning: { effort: "medium" },
|
|
69
|
+
text: { format: { type: "json_schema", name: "Meal", schema } },
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const { data } = await mealEnricher.runJson<{ meal: string }>({ input: "two eggs" });
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Set a field on `run` or `runJson` to override its registered default. `strict: false` still requires parsed JSON but does not require that value to match the schema.
|
|
77
|
+
|
|
36
78
|
Env: `AGENTLU_URL` (default `http://localhost:8912/v1`), `AGENTLU_API_KEY`.
|
|
79
|
+
|
|
80
|
+
## Decisions (System One / Jev)
|
|
81
|
+
|
|
82
|
+
`decisions` is a different primitive from `run`: Jev returns typed, calibrated
|
|
83
|
+
probabilities - never text - in ~100-500ms. Use it for routing, classification,
|
|
84
|
+
scoring and gating inside your own code.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { AgentLu, noul, choice, score } from "@betterlu/agentlu";
|
|
88
|
+
|
|
89
|
+
const alu = new AgentLu();
|
|
90
|
+
alu.apiKey = key;
|
|
91
|
+
|
|
92
|
+
const { answers } = await alu.decisions.create({
|
|
93
|
+
state: "I was charged twice and I'm losing sales. Please fix this ASAP.",
|
|
94
|
+
questions: {
|
|
95
|
+
urgent: noul("Does this need urgent attention?"),
|
|
96
|
+
team: choice("Which team should handle it?", {
|
|
97
|
+
billing: "charges and payments",
|
|
98
|
+
shipping: "delivery",
|
|
99
|
+
}),
|
|
100
|
+
frustration: score("How frustrated is the customer?", ["Calm", "Frustrated", "Very angry"]),
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (answers.urgent.type === "noul" && answers.urgent.noul > 0.8) routeToHuman();
|
|
105
|
+
if (answers.team.type === "choice") return lookupOrder(answers.team.choice);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`answers.<name>` is a discriminated union on `type`:
|
|
109
|
+
|
|
110
|
+
| Question | Answer fields |
|
|
111
|
+
|---|---|
|
|
112
|
+
| `noul` | `noul` - probability that the answer is yes (0..1) |
|
|
113
|
+
| `choice` | `choice`, `confidence`, `probabilities` per option |
|
|
114
|
+
| `score` | `score`, `confidence`, `legend`, `probabilities` per step |
|
|
115
|
+
|
|
116
|
+
`decisions.ask()` is the same call returning only the answers map. Discover the
|
|
117
|
+
model in `GET /v1/models` (`harness: "systemone"`); it is only callable on
|
|
118
|
+
`POST /v1/decisions`, sync, no streaming. A missing key surfaces as HTTP 503
|
|
119
|
+
`systemone_unconfigured` (not retried by the SDK).
|
|
120
|
+
|
|
121
|
+
## Browser handoff
|
|
122
|
+
|
|
123
|
+
The `browser` family reuses a persistent, headful BrowserLu session. Scope it
|
|
124
|
+
to the operations the caller needs and identify the linked household person:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
const { response } = await alu.run({
|
|
128
|
+
agent: "api",
|
|
129
|
+
person: "larry",
|
|
130
|
+
tools: [{ type: "browser", allowed_tools: ["browser_open", "browser_snapshot"] }],
|
|
131
|
+
input: "Read the current page and summarize it.",
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (response.agentlu?.browser?.status === "needs_human") {
|
|
135
|
+
// Let this request end. The human completes login/MFA in the AgentLu viewer,
|
|
136
|
+
// then send a new request with the same conversation/session context.
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Browser response metadata contains the session and page identity plus screenshot
|
|
141
|
+
ids. Screenshot pixels stay in the authenticated viewer and are not copied into
|
|
142
|
+
API response metadata.
|
package/dist/index.d.ts
CHANGED
|
@@ -6,10 +6,155 @@
|
|
|
6
6
|
* new OpenAI({ baseURL: "http://localhost:8912/v1", apiKey })
|
|
7
7
|
*
|
|
8
8
|
* This client exists for the ergonomic bits OpenAI's SDK does not know about:
|
|
9
|
-
* get-or-create registration, typed `
|
|
9
|
+
* get-or-create registration, typed `runJson`, and auto-retry on
|
|
10
10
|
* `409 thread_busy`.
|
|
11
11
|
*/
|
|
12
12
|
export declare const DEFAULT_BASE_URL = "http://localhost:8912/v1";
|
|
13
|
+
export type Backend = "pi";
|
|
14
|
+
export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
15
|
+
export type ApiToolFamily = "web_search" | "fetch_content" | "hublu" | "memory" | "image" | "browser" | "android";
|
|
16
|
+
/** Closed tool-family request accepted by `/v1/responses`. */
|
|
17
|
+
export interface ApiToolSpec {
|
|
18
|
+
type: ApiToolFamily;
|
|
19
|
+
/** Optional family-specific operation allowlist (HubLu, BrowserLu, or AndroidLu). */
|
|
20
|
+
allowed_tools?: string[];
|
|
21
|
+
}
|
|
22
|
+
export type ApiToolInput = ApiToolFamily | ApiToolSpec;
|
|
23
|
+
export type BrowserStatus = "active" | "needs_human" | "human_control" | "idle" | "closed" | "interrupted";
|
|
24
|
+
export interface BrowserScreenshot {
|
|
25
|
+
id: string;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
width: number;
|
|
28
|
+
height: number;
|
|
29
|
+
}
|
|
30
|
+
export interface BrowserActivity {
|
|
31
|
+
sessionId: string;
|
|
32
|
+
username: "larry" | "misa";
|
|
33
|
+
ownerId?: string;
|
|
34
|
+
tabId?: string;
|
|
35
|
+
url?: string;
|
|
36
|
+
title?: string;
|
|
37
|
+
status: BrowserStatus;
|
|
38
|
+
action?: string;
|
|
39
|
+
reason?: string;
|
|
40
|
+
observationId?: string;
|
|
41
|
+
screenshots?: BrowserScreenshot[];
|
|
42
|
+
}
|
|
43
|
+
export type AndroidStatus = "active" | "needs_human" | "human_control" | "idle" | "closed" | "interrupted" | "reserved";
|
|
44
|
+
export type AndroidOperationState = "running" | "succeeded" | "failed" | "unknown";
|
|
45
|
+
export interface AndroidOperation {
|
|
46
|
+
operationId: string;
|
|
47
|
+
kind: string;
|
|
48
|
+
state: AndroidOperationState;
|
|
49
|
+
dispatchedAt: string;
|
|
50
|
+
settledAt?: string;
|
|
51
|
+
outcome?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface AndroidActivity {
|
|
54
|
+
sessionId: string;
|
|
55
|
+
username: "larry" | "misa";
|
|
56
|
+
ownerId?: string;
|
|
57
|
+
status: AndroidStatus;
|
|
58
|
+
action?: string;
|
|
59
|
+
reason?: string;
|
|
60
|
+
observationId?: string;
|
|
61
|
+
leaseId?: string;
|
|
62
|
+
readiness?: "stopped" | "booting" | "ready" | "failed";
|
|
63
|
+
control?: "idle" | "agent" | "needs_human" | "human" | "reserved" | "interrupted";
|
|
64
|
+
foregroundPackage?: string;
|
|
65
|
+
pendingOperation?: AndroidOperation;
|
|
66
|
+
lastOperation?: AndroidOperation;
|
|
67
|
+
viewerPath?: string;
|
|
68
|
+
reservationExpiresAt?: string;
|
|
69
|
+
}
|
|
70
|
+
export interface ReasoningOptions {
|
|
71
|
+
effort?: ThinkingLevel;
|
|
72
|
+
}
|
|
73
|
+
export interface ClientReasoningOptions {
|
|
74
|
+
effort: ThinkingLevel;
|
|
75
|
+
}
|
|
76
|
+
export interface TextFormatText {
|
|
77
|
+
type: "text";
|
|
78
|
+
}
|
|
79
|
+
export interface TextFormatJsonSchema {
|
|
80
|
+
type: "json_schema";
|
|
81
|
+
name: string;
|
|
82
|
+
schema: Record<string, unknown>;
|
|
83
|
+
strict?: boolean;
|
|
84
|
+
description?: string;
|
|
85
|
+
}
|
|
86
|
+
export type TextFormat = TextFormatText | TextFormatJsonSchema;
|
|
87
|
+
export interface TextConfig {
|
|
88
|
+
format?: TextFormat;
|
|
89
|
+
}
|
|
90
|
+
/** Yes/no, answered as the probability that the answer is yes. */
|
|
91
|
+
export interface NoulQuestion {
|
|
92
|
+
type: "noul";
|
|
93
|
+
instructions: string;
|
|
94
|
+
/** Optional descriptions of the true/false poles (calibrates the answer). */
|
|
95
|
+
criteria?: {
|
|
96
|
+
true: string;
|
|
97
|
+
false: string;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export interface ChoiceQuestion {
|
|
101
|
+
type: "choice";
|
|
102
|
+
instructions: string;
|
|
103
|
+
/** Option name -> description. At least two options. */
|
|
104
|
+
criteria: Record<string, string>;
|
|
105
|
+
}
|
|
106
|
+
export interface ScoreQuestion {
|
|
107
|
+
type: "score";
|
|
108
|
+
instructions: string;
|
|
109
|
+
/** Ordered levels, lowest first. At least two. */
|
|
110
|
+
criteria: string[];
|
|
111
|
+
}
|
|
112
|
+
export type DecisionQuestion = NoulQuestion | ChoiceQuestion | ScoreQuestion;
|
|
113
|
+
export interface NoulAnswer {
|
|
114
|
+
type: "noul";
|
|
115
|
+
noul: number;
|
|
116
|
+
}
|
|
117
|
+
export interface ChoiceAnswer {
|
|
118
|
+
type: "choice";
|
|
119
|
+
choice: string;
|
|
120
|
+
confidence: number;
|
|
121
|
+
probabilities: Record<string, number>;
|
|
122
|
+
}
|
|
123
|
+
export interface ScoreAnswer {
|
|
124
|
+
type: "score";
|
|
125
|
+
score: number;
|
|
126
|
+
confidence: number;
|
|
127
|
+
legend?: Record<string, string>;
|
|
128
|
+
probabilities: Record<string, number>;
|
|
129
|
+
}
|
|
130
|
+
export type DecisionAnswer = NoulAnswer | ChoiceAnswer | ScoreAnswer;
|
|
131
|
+
export interface DecisionOptions {
|
|
132
|
+
/** Unstructured input: a text state, or a JSON object (serialized upstream). */
|
|
133
|
+
state: string | Record<string, unknown>;
|
|
134
|
+
questions: Record<string, DecisionQuestion>;
|
|
135
|
+
/** Decision model id (default: the configured `typesafe/jev`). */
|
|
136
|
+
model?: string;
|
|
137
|
+
}
|
|
138
|
+
export interface DecisionObject {
|
|
139
|
+
id: string;
|
|
140
|
+
object: "decision";
|
|
141
|
+
created_at: number;
|
|
142
|
+
model: string;
|
|
143
|
+
answers: Record<string, DecisionAnswer>;
|
|
144
|
+
usage: {
|
|
145
|
+
input_tokens: number;
|
|
146
|
+
output_tokens: number;
|
|
147
|
+
total_tokens: number;
|
|
148
|
+
} | null;
|
|
149
|
+
[k: string]: unknown;
|
|
150
|
+
}
|
|
151
|
+
/** Question builders; sugar over the wire shape. */
|
|
152
|
+
export declare const noul: (instructions: string, criteria?: {
|
|
153
|
+
true: string;
|
|
154
|
+
false: string;
|
|
155
|
+
}) => NoulQuestion;
|
|
156
|
+
export declare const choice: (instructions: string, criteria: Record<string, string>) => ChoiceQuestion;
|
|
157
|
+
export declare const score: (instructions: string, levels: string[]) => ScoreQuestion;
|
|
13
158
|
export declare class AgentLuError extends Error {
|
|
14
159
|
readonly status: number;
|
|
15
160
|
readonly code: string;
|
|
@@ -20,42 +165,57 @@ export declare class AgentLuError extends Error {
|
|
|
20
165
|
/** Parsed Retry-After header, in milliseconds, when present. */
|
|
21
166
|
retryAfterMs?: number | undefined);
|
|
22
167
|
}
|
|
168
|
+
export declare class AgentLuResponseError extends AgentLuError {
|
|
169
|
+
readonly response: ResponseObject;
|
|
170
|
+
readonly responseStatus: string;
|
|
171
|
+
constructor(response: ResponseObject);
|
|
172
|
+
}
|
|
23
173
|
export interface AgentLuOptions {
|
|
24
174
|
baseUrl?: string;
|
|
25
175
|
apiKey?: string;
|
|
26
176
|
fetch?: typeof fetch;
|
|
27
177
|
}
|
|
28
178
|
export interface ClientDefaults {
|
|
179
|
+
backend?: Backend;
|
|
29
180
|
agent?: string;
|
|
30
181
|
model?: string;
|
|
31
182
|
instructions?: string;
|
|
32
183
|
temperature?: number;
|
|
33
|
-
|
|
34
|
-
tools?: Array<string | {
|
|
35
|
-
type: string;
|
|
36
|
-
allowed_tools?: string[];
|
|
37
|
-
}>;
|
|
184
|
+
tools?: ApiToolInput[];
|
|
38
185
|
timeoutMs?: number;
|
|
186
|
+
store?: boolean;
|
|
187
|
+
reasoning?: ClientReasoningOptions;
|
|
188
|
+
text?: TextConfig;
|
|
189
|
+
}
|
|
190
|
+
/** High-level use-case declaration for model routing and priority tier. */
|
|
191
|
+
export interface UseCaseProfile {
|
|
192
|
+
inputModalities?: Array<"text" | "image" | "video">;
|
|
193
|
+
outputKind?: "text" | "json_schema";
|
|
194
|
+
/** MiniMax priority admission consent (priority costs 1.5x standard). */
|
|
195
|
+
priorityTier?: "never" | "retry" | "first" | "always";
|
|
39
196
|
}
|
|
40
197
|
export interface RunOptions {
|
|
198
|
+
backend?: Backend;
|
|
41
199
|
agent?: string;
|
|
42
200
|
model?: string;
|
|
43
201
|
input?: string;
|
|
44
202
|
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
203
|
+
* Attachments previously uploaded via `upload()`. Prepended to the user
|
|
204
|
+
* turn using each file's `kind` (`input_image` / `input_video` / `input_file`).
|
|
205
|
+
*/
|
|
48
206
|
fileIds?: string[];
|
|
207
|
+
/** Optional kinds aligned with `fileIds` (from `upload()`). Defaults to image. */
|
|
208
|
+
fileKinds?: Array<"image" | "video" | "text" | "document">;
|
|
49
209
|
/** Structured input messages (OpenAI shape) - overrides `input`. */
|
|
50
210
|
inputMessages?: Array<{
|
|
51
211
|
role: string;
|
|
52
212
|
content: string | Array<Record<string, unknown>>;
|
|
53
213
|
}>;
|
|
54
214
|
instructions?: string;
|
|
55
|
-
tools?:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
215
|
+
tools?: ApiToolInput[];
|
|
216
|
+
reasoning?: ReasoningOptions;
|
|
217
|
+
text?: TextConfig;
|
|
218
|
+
strict?: boolean;
|
|
59
219
|
conversation?: string;
|
|
60
220
|
store?: boolean;
|
|
61
221
|
person?: string | null;
|
|
@@ -76,6 +236,7 @@ export interface RunOptions {
|
|
|
76
236
|
export interface ResponseObject {
|
|
77
237
|
id: string;
|
|
78
238
|
status: string;
|
|
239
|
+
backend: Backend;
|
|
79
240
|
output_text: string;
|
|
80
241
|
output_parsed?: unknown;
|
|
81
242
|
conversation?: {
|
|
@@ -86,8 +247,19 @@ export interface ResponseObject {
|
|
|
86
247
|
message: string;
|
|
87
248
|
retryable: boolean;
|
|
88
249
|
} | null;
|
|
250
|
+
incomplete_details?: {
|
|
251
|
+
reason: string;
|
|
252
|
+
} | null;
|
|
89
253
|
agentlu?: {
|
|
90
254
|
session_url?: string | null;
|
|
255
|
+
/** Model the runtime actually served (requested `model` may have fallen back). */
|
|
256
|
+
effective_model?: string | null;
|
|
257
|
+
/** MiniMax admission tier requested for the winning attempt, when any. */
|
|
258
|
+
service_tier?: "standard" | "priority";
|
|
259
|
+
/** Latest persistent BrowserLu activity; screenshot bytes are never in this object. */
|
|
260
|
+
browser?: BrowserActivity;
|
|
261
|
+
/** Latest persistent AndroidLu activity; screenshot bytes are never in this object. */
|
|
262
|
+
android?: AndroidActivity;
|
|
91
263
|
};
|
|
92
264
|
[k: string]: unknown;
|
|
93
265
|
}
|
|
@@ -96,11 +268,13 @@ export declare class AgentLu {
|
|
|
96
268
|
apiKey: string | undefined;
|
|
97
269
|
private readonly fetchImpl;
|
|
98
270
|
constructor(opts?: AgentLuOptions);
|
|
271
|
+
private postClient;
|
|
99
272
|
/** Get-or-create a client by name. Caches the returned key on this instance. */
|
|
100
273
|
register(name: string, opts?: {
|
|
101
274
|
person?: string | null;
|
|
102
275
|
instructions?: string | null;
|
|
103
276
|
defaults?: ClientDefaults;
|
|
277
|
+
use_case_profile?: UseCaseProfile;
|
|
104
278
|
}): Promise<ResponseObject & {
|
|
105
279
|
api_key?: string;
|
|
106
280
|
name: string;
|
|
@@ -111,10 +285,11 @@ export declare class AgentLu {
|
|
|
111
285
|
* The use-case's defaults (system prompt, tools, temperature, ...) live
|
|
112
286
|
* server-side on the client, so `run()` only needs the input. Registering a
|
|
113
287
|
* use-case is registering a client - `alu.useCase("mealu-enrich", {...})`.
|
|
114
|
-
|
|
288
|
+
*/
|
|
115
289
|
useCase(name: string, opts?: {
|
|
116
290
|
person?: string | null;
|
|
117
291
|
defaults?: ClientDefaults;
|
|
292
|
+
use_case_profile?: UseCaseProfile;
|
|
118
293
|
}): UseCaseRunner;
|
|
119
294
|
/**
|
|
120
295
|
* Register a use-case client and resolve to its api key. The key is cached
|
|
@@ -124,14 +299,18 @@ export declare class AgentLu {
|
|
|
124
299
|
useCaseClient(name: string, opts?: {
|
|
125
300
|
person?: string | null;
|
|
126
301
|
defaults?: ClientDefaults;
|
|
302
|
+
use_case_profile?: UseCaseProfile;
|
|
127
303
|
}): Promise<string>;
|
|
304
|
+
private registerForUseCase;
|
|
128
305
|
/**
|
|
129
|
-
* Upload an image
|
|
130
|
-
*
|
|
306
|
+
* Upload an image, video, or document for use with `run({ fileIds })`.
|
|
307
|
+
* Returns the `file_id` and stored `kind`.
|
|
131
308
|
*/
|
|
132
309
|
upload(data: Uint8Array | Blob, filename: string, mime?: string): Promise<{
|
|
133
310
|
id: string;
|
|
311
|
+
kind?: string;
|
|
134
312
|
}>;
|
|
313
|
+
private uploadForApiKey;
|
|
135
314
|
me(): Promise<unknown>;
|
|
136
315
|
/**
|
|
137
316
|
* One-shot helper. When `schema` is given, returns the validated object as
|
|
@@ -142,6 +321,13 @@ export declare class AgentLu {
|
|
|
142
321
|
data?: T;
|
|
143
322
|
response: ResponseObject;
|
|
144
323
|
}>;
|
|
324
|
+
runJson<T = unknown>(opts: RunOptions): Promise<{
|
|
325
|
+
data: T;
|
|
326
|
+
response: ResponseObject;
|
|
327
|
+
}>;
|
|
328
|
+
private runForApiKey;
|
|
329
|
+
private runJsonForApiKey;
|
|
330
|
+
private createRunResponse;
|
|
145
331
|
readonly responses: {
|
|
146
332
|
create: (body: Record<string, unknown>) => Promise<ResponseObject>;
|
|
147
333
|
retrieve: (id: string) => Promise<ResponseObject>;
|
|
@@ -153,8 +339,9 @@ export declare class AgentLu {
|
|
|
153
339
|
object: string;
|
|
154
340
|
data: ResponseObject[];
|
|
155
341
|
}>;
|
|
156
|
-
stream: (body: Record<string, unknown>) => AsyncGenerator<Record<string, unknown
|
|
342
|
+
stream: (body: Record<string, unknown>) => AsyncGenerator<Record<string, unknown>, any, any>;
|
|
157
343
|
};
|
|
344
|
+
private streamForApiKey;
|
|
158
345
|
readonly threads: {
|
|
159
346
|
create: (body?: Record<string, unknown>) => Promise<unknown>;
|
|
160
347
|
retrieve: (id: string) => Promise<unknown>;
|
|
@@ -162,6 +349,24 @@ export declare class AgentLu {
|
|
|
162
349
|
delete: (id: string) => Promise<unknown>;
|
|
163
350
|
messages: (id: string) => Promise<unknown>;
|
|
164
351
|
};
|
|
352
|
+
/**
|
|
353
|
+
* Decision model (System One / Jev): typed questions in, calibrated
|
|
354
|
+
* probabilities out, ~100-500ms. Sync only - there is no stream or poll.
|
|
355
|
+
*
|
|
356
|
+
* const { answers } = await alu.decisions.create({
|
|
357
|
+
* state: "I was charged twice and I'm losing sales. Please fix this ASAP.",
|
|
358
|
+
* questions: {
|
|
359
|
+
* urgent: noul("Does this need urgent attention?"),
|
|
360
|
+
* team: choice("Which team should handle it?", { billing: "charges", shipping: "delivery" }),
|
|
361
|
+
* },
|
|
362
|
+
* });
|
|
363
|
+
* if (answers.urgent.noul > 0.8) routeToHuman();
|
|
364
|
+
*/
|
|
365
|
+
readonly decisions: {
|
|
366
|
+
create: (body: DecisionOptions) => Promise<DecisionObject>;
|
|
367
|
+
/** Same call, returning only the answers map. */
|
|
368
|
+
ask: (body: DecisionOptions) => Promise<Record<string, DecisionAnswer>>;
|
|
369
|
+
};
|
|
165
370
|
agents(): Promise<unknown>;
|
|
166
371
|
models(): Promise<unknown>;
|
|
167
372
|
tools(): Promise<unknown>;
|
|
@@ -181,14 +386,15 @@ export declare class AgentLu {
|
|
|
181
386
|
* const { data } = await enrich.run({ input: "two eggs and toast" });
|
|
182
387
|
*/
|
|
183
388
|
export declare class UseCaseRunner {
|
|
184
|
-
private readonly parent;
|
|
185
389
|
readonly name: string;
|
|
186
390
|
private readonly registerOpts;
|
|
391
|
+
private readonly operations;
|
|
187
392
|
private apiKey;
|
|
188
393
|
private ready;
|
|
189
394
|
constructor(parent: AgentLu, name: string, opts?: {
|
|
190
395
|
person?: string | null;
|
|
191
396
|
defaults?: ClientDefaults;
|
|
397
|
+
use_case_profile?: UseCaseProfile;
|
|
192
398
|
});
|
|
193
399
|
/** Idempotent get-or-create; safe to call before every run. */
|
|
194
400
|
private ensure;
|
|
@@ -200,5 +406,17 @@ export declare class UseCaseRunner {
|
|
|
200
406
|
data?: T;
|
|
201
407
|
response: ResponseObject;
|
|
202
408
|
}>;
|
|
409
|
+
runJson<T = unknown>(opts: RunOptions): Promise<{
|
|
410
|
+
data: T;
|
|
411
|
+
response: ResponseObject;
|
|
412
|
+
}>;
|
|
203
413
|
stream(body: Record<string, unknown>): Promise<AsyncGenerator<Record<string, unknown>, any, any>>;
|
|
414
|
+
/**
|
|
415
|
+
* Upload with this use-case's key, so the file is owned by the same
|
|
416
|
+
* service client that will reference it via `run({ fileIds })`.
|
|
417
|
+
*/
|
|
418
|
+
upload(data: Uint8Array | Blob, filename: string, mime?: string): Promise<{
|
|
419
|
+
id: string;
|
|
420
|
+
kind?: string;
|
|
421
|
+
}>;
|
|
204
422
|
}
|
package/dist/index.js
CHANGED
|
@@ -6,10 +6,26 @@
|
|
|
6
6
|
* new OpenAI({ baseURL: "http://localhost:8912/v1", apiKey })
|
|
7
7
|
*
|
|
8
8
|
* This client exists for the ergonomic bits OpenAI's SDK does not know about:
|
|
9
|
-
* get-or-create registration, typed `
|
|
9
|
+
* get-or-create registration, typed `runJson`, and auto-retry on
|
|
10
10
|
* `409 thread_busy`.
|
|
11
11
|
*/
|
|
12
12
|
export const DEFAULT_BASE_URL = "http://localhost:8912/v1";
|
|
13
|
+
/** Question builders; sugar over the wire shape. */
|
|
14
|
+
export const noul = (instructions, criteria) => ({
|
|
15
|
+
type: "noul",
|
|
16
|
+
instructions,
|
|
17
|
+
...(criteria ? { criteria } : {}),
|
|
18
|
+
});
|
|
19
|
+
export const choice = (instructions, criteria) => ({
|
|
20
|
+
type: "choice",
|
|
21
|
+
instructions,
|
|
22
|
+
criteria,
|
|
23
|
+
});
|
|
24
|
+
export const score = (instructions, levels) => ({
|
|
25
|
+
type: "score",
|
|
26
|
+
instructions,
|
|
27
|
+
criteria: levels,
|
|
28
|
+
});
|
|
13
29
|
export class AgentLuError extends Error {
|
|
14
30
|
status;
|
|
15
31
|
code;
|
|
@@ -26,9 +42,23 @@ export class AgentLuError extends Error {
|
|
|
26
42
|
this.name = "AgentLuError";
|
|
27
43
|
}
|
|
28
44
|
}
|
|
45
|
+
export class AgentLuResponseError extends AgentLuError {
|
|
46
|
+
response;
|
|
47
|
+
responseStatus;
|
|
48
|
+
constructor(response) {
|
|
49
|
+
const responseStatus = response.status;
|
|
50
|
+
const code = response.error?.code ?? responseStatus;
|
|
51
|
+
const message = response.error?.message ?? `Response status "${responseStatus}" is not completed.`;
|
|
52
|
+
super(200, code, message, response.error?.retryable ?? false);
|
|
53
|
+
this.name = "AgentLuResponseError";
|
|
54
|
+
this.response = response;
|
|
55
|
+
this.responseStatus = responseStatus;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
29
58
|
function env(name) {
|
|
30
59
|
return typeof process !== "undefined" ? process.env[name] : undefined;
|
|
31
60
|
}
|
|
61
|
+
const scopedUseCaseOperations = new WeakMap();
|
|
32
62
|
export class AgentLu {
|
|
33
63
|
baseUrl;
|
|
34
64
|
apiKey;
|
|
@@ -37,10 +67,20 @@ export class AgentLu {
|
|
|
37
67
|
this.baseUrl = (opts.baseUrl ?? env("AGENTLU_URL") ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
38
68
|
this.apiKey = opts.apiKey ?? env("AGENTLU_API_KEY");
|
|
39
69
|
this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
70
|
+
scopedUseCaseOperations.set(this, {
|
|
71
|
+
register: (name, registerOpts) => this.registerForUseCase(name, registerOpts),
|
|
72
|
+
run: (apiKey, runOpts) => this.runForApiKey(apiKey, runOpts),
|
|
73
|
+
runJson: (apiKey, runOpts) => this.runJsonForApiKey(apiKey, runOpts),
|
|
74
|
+
stream: (apiKey, body) => this.streamForApiKey(body, apiKey),
|
|
75
|
+
upload: (apiKey, data, filename, mime) => this.uploadForApiKey(data, filename, mime, apiKey),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
postClient(name, opts) {
|
|
79
|
+
return this.request("POST", "/clients", { name, ...opts }, { auth: false });
|
|
40
80
|
}
|
|
41
81
|
/** Get-or-create a client by name. Caches the returned key on this instance. */
|
|
42
82
|
async register(name, opts) {
|
|
43
|
-
const res = await this.
|
|
83
|
+
const res = await this.postClient(name, opts);
|
|
44
84
|
if (res.api_key)
|
|
45
85
|
this.apiKey = res.api_key;
|
|
46
86
|
return res;
|
|
@@ -51,7 +91,7 @@ export class AgentLu {
|
|
|
51
91
|
* The use-case's defaults (system prompt, tools, temperature, ...) live
|
|
52
92
|
* server-side on the client, so `run()` only needs the input. Registering a
|
|
53
93
|
* use-case is registering a client - `alu.useCase("mealu-enrich", {...})`.
|
|
54
|
-
|
|
94
|
+
*/
|
|
55
95
|
useCase(name, opts) {
|
|
56
96
|
return new UseCaseRunner(this, name, opts);
|
|
57
97
|
}
|
|
@@ -61,21 +101,30 @@ export class AgentLu {
|
|
|
61
101
|
* `responses.create` is authorized.
|
|
62
102
|
*/
|
|
63
103
|
async useCaseClient(name, opts) {
|
|
64
|
-
const res = await this.
|
|
104
|
+
const res = await this.postClient(name, opts);
|
|
65
105
|
if (!res.api_key)
|
|
66
106
|
throw new AgentLuError(500, "internal_error", "register returned no key", true);
|
|
67
107
|
this.apiKey = res.api_key;
|
|
68
108
|
return res.api_key;
|
|
69
109
|
}
|
|
110
|
+
async registerForUseCase(name, opts) {
|
|
111
|
+
const res = await this.postClient(name, opts);
|
|
112
|
+
if (!res.api_key)
|
|
113
|
+
throw new AgentLuError(500, "internal_error", "register returned no key", true);
|
|
114
|
+
return res.api_key;
|
|
115
|
+
}
|
|
70
116
|
/**
|
|
71
|
-
* Upload an image
|
|
72
|
-
*
|
|
117
|
+
* Upload an image, video, or document for use with `run({ fileIds })`.
|
|
118
|
+
* Returns the `file_id` and stored `kind`.
|
|
73
119
|
*/
|
|
74
120
|
async upload(data, filename, mime) {
|
|
121
|
+
return this.uploadForApiKey(data, filename, mime, this.apiKey);
|
|
122
|
+
}
|
|
123
|
+
async uploadForApiKey(data, filename, mime, apiKey) {
|
|
75
124
|
const form = new FormData();
|
|
76
125
|
const blob = data instanceof Blob ? data : new Blob([new Uint8Array(data)], { type: mime ?? "application/octet-stream" });
|
|
77
126
|
form.append("file", blob, filename);
|
|
78
|
-
const res = await this.raw("POST", "/files", form);
|
|
127
|
+
const res = await this.raw("POST", "/files", form, { apiKey });
|
|
79
128
|
return (await res.json());
|
|
80
129
|
}
|
|
81
130
|
async me() {
|
|
@@ -87,13 +136,43 @@ export class AgentLu {
|
|
|
87
136
|
* could not produce matching JSON.
|
|
88
137
|
*/
|
|
89
138
|
async run(opts) {
|
|
139
|
+
return this.runForApiKey(this.apiKey, opts);
|
|
140
|
+
}
|
|
141
|
+
async runJson(opts) {
|
|
142
|
+
assertJsonRunOptions(opts);
|
|
143
|
+
return this.runJsonForApiKey(this.apiKey, opts);
|
|
144
|
+
}
|
|
145
|
+
async runForApiKey(apiKey, opts) {
|
|
146
|
+
const response = await this.createRunResponse(opts, apiKey);
|
|
147
|
+
if (response.status === "failed" && response.error) {
|
|
148
|
+
throw new AgentLuError(200, response.error.code, response.error.message, response.error.retryable);
|
|
149
|
+
}
|
|
150
|
+
return { data: response.output_parsed, response };
|
|
151
|
+
}
|
|
152
|
+
async runJsonForApiKey(apiKey, opts) {
|
|
153
|
+
const response = await this.createRunResponse(opts, apiKey);
|
|
154
|
+
if (response.status !== "completed")
|
|
155
|
+
throw new AgentLuResponseError(response);
|
|
156
|
+
if (!Object.prototype.hasOwnProperty.call(response, "output_parsed")) {
|
|
157
|
+
throw new AgentLuError(200, "missing_output_parsed", "Completed response did not include output_parsed.", false);
|
|
158
|
+
}
|
|
159
|
+
return { data: response.output_parsed, response };
|
|
160
|
+
}
|
|
161
|
+
async createRunResponse(opts, apiKey) {
|
|
90
162
|
let input = opts.inputMessages ?? opts.input;
|
|
91
163
|
if (opts.fileIds?.length) {
|
|
92
164
|
input = [
|
|
93
165
|
{
|
|
94
166
|
role: "user",
|
|
95
167
|
content: [
|
|
96
|
-
...opts.fileIds.map((file_id) =>
|
|
168
|
+
...opts.fileIds.map((file_id, i) => {
|
|
169
|
+
const kind = opts.fileKinds?.[i];
|
|
170
|
+
if (kind === "video")
|
|
171
|
+
return { type: "input_video", file_id };
|
|
172
|
+
if (kind === "text" || kind === "document")
|
|
173
|
+
return { type: "input_file", file_id };
|
|
174
|
+
return { type: "input_image", file_id };
|
|
175
|
+
}),
|
|
97
176
|
{ type: "input_text", text: opts.input ?? "" },
|
|
98
177
|
],
|
|
99
178
|
},
|
|
@@ -101,10 +180,12 @@ export class AgentLu {
|
|
|
101
180
|
}
|
|
102
181
|
const body = {
|
|
103
182
|
input,
|
|
183
|
+
backend: opts.backend,
|
|
104
184
|
agent: opts.agent,
|
|
105
185
|
model: opts.model,
|
|
106
186
|
instructions: opts.instructions,
|
|
107
187
|
tools: opts.tools,
|
|
188
|
+
reasoning: opts.reasoning,
|
|
108
189
|
conversation: opts.conversation,
|
|
109
190
|
store: opts.store,
|
|
110
191
|
person: opts.person,
|
|
@@ -117,21 +198,20 @@ export class AgentLu {
|
|
|
117
198
|
background: opts.background,
|
|
118
199
|
metadata: opts.metadata,
|
|
119
200
|
};
|
|
120
|
-
if (opts.
|
|
201
|
+
if (opts.text !== undefined) {
|
|
202
|
+
body.text = opts.text;
|
|
203
|
+
}
|
|
204
|
+
else if (opts.schema !== undefined) {
|
|
121
205
|
body.text = {
|
|
122
206
|
format: {
|
|
123
207
|
type: "json_schema",
|
|
124
208
|
name: opts.schemaName ?? "Result",
|
|
125
209
|
schema: opts.schema,
|
|
126
|
-
strict: true,
|
|
210
|
+
strict: opts.strict ?? true,
|
|
127
211
|
},
|
|
128
212
|
};
|
|
129
213
|
}
|
|
130
|
-
|
|
131
|
-
if (response.status === "failed" && response.error) {
|
|
132
|
-
throw new AgentLuError(200, response.error.code, response.error.message, response.error.retryable);
|
|
133
|
-
}
|
|
134
|
-
return { data: response.output_parsed, response };
|
|
214
|
+
return this.request("POST", "/responses", body, { apiKey });
|
|
135
215
|
}
|
|
136
216
|
responses = {
|
|
137
217
|
create: (body) => this.request("POST", "/responses", body),
|
|
@@ -146,42 +226,43 @@ export class AgentLu {
|
|
|
146
226
|
const qs = q.toString();
|
|
147
227
|
return this.request("GET", `/responses${qs ? `?${qs}` : ""}`);
|
|
148
228
|
},
|
|
149
|
-
stream:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
229
|
+
stream: (body) => this.streamForApiKey(body),
|
|
230
|
+
};
|
|
231
|
+
async *streamForApiKey(body, apiKey) {
|
|
232
|
+
const res = await this.raw("POST", "/responses", { ...body, stream: true }, { apiKey });
|
|
233
|
+
if (!res.body)
|
|
234
|
+
throw new AgentLuError(0, "internal_error", "empty stream body", true);
|
|
235
|
+
const reader = res.body.getReader();
|
|
236
|
+
const decoder = new TextDecoder();
|
|
237
|
+
let buf = "";
|
|
238
|
+
try {
|
|
239
|
+
for (;;) {
|
|
240
|
+
const { done, value } = await reader.read();
|
|
241
|
+
if (done)
|
|
242
|
+
break;
|
|
243
|
+
buf += decoder.decode(value, { stream: true });
|
|
244
|
+
const lines = buf.split("\n");
|
|
245
|
+
buf = lines.pop() ?? "";
|
|
246
|
+
for (const line of lines) {
|
|
247
|
+
const trimmed = line.trim();
|
|
248
|
+
if (!trimmed.startsWith("data: "))
|
|
249
|
+
continue;
|
|
250
|
+
const payload = trimmed.slice(6);
|
|
251
|
+
if (payload === "[DONE]")
|
|
252
|
+
return;
|
|
253
|
+
try {
|
|
254
|
+
yield JSON.parse(payload);
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
/* ignore malformed chunks */
|
|
177
258
|
}
|
|
178
259
|
}
|
|
179
260
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
}
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
reader.releaseLock();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
185
266
|
threads = {
|
|
186
267
|
create: (body = {}) => this.request("POST", "/threads", body),
|
|
187
268
|
retrieve: (id) => this.request("GET", `/threads/${encodeURIComponent(id)}`),
|
|
@@ -189,6 +270,24 @@ export class AgentLu {
|
|
|
189
270
|
delete: (id) => this.request("DELETE", `/threads/${encodeURIComponent(id)}`),
|
|
190
271
|
messages: (id) => this.request("GET", `/threads/${encodeURIComponent(id)}/messages`),
|
|
191
272
|
};
|
|
273
|
+
/**
|
|
274
|
+
* Decision model (System One / Jev): typed questions in, calibrated
|
|
275
|
+
* probabilities out, ~100-500ms. Sync only - there is no stream or poll.
|
|
276
|
+
*
|
|
277
|
+
* const { answers } = await alu.decisions.create({
|
|
278
|
+
* state: "I was charged twice and I'm losing sales. Please fix this ASAP.",
|
|
279
|
+
* questions: {
|
|
280
|
+
* urgent: noul("Does this need urgent attention?"),
|
|
281
|
+
* team: choice("Which team should handle it?", { billing: "charges", shipping: "delivery" }),
|
|
282
|
+
* },
|
|
283
|
+
* });
|
|
284
|
+
* if (answers.urgent.noul > 0.8) routeToHuman();
|
|
285
|
+
*/
|
|
286
|
+
decisions = {
|
|
287
|
+
create: (body) => this.request("POST", "/decisions", body),
|
|
288
|
+
/** Same call, returning only the answers map. */
|
|
289
|
+
ask: async (body) => (await this.request("POST", "/decisions", body)).answers,
|
|
290
|
+
};
|
|
192
291
|
async agents() {
|
|
193
292
|
return this.request("GET", "/agents", undefined, { auth: false });
|
|
194
293
|
}
|
|
@@ -205,6 +304,9 @@ export class AgentLu {
|
|
|
205
304
|
// `409 thread_busy` and `503 too_many_active_runs` are correctness bounds,
|
|
206
305
|
// not rate limits. Retrying with backoff is almost always the right thing,
|
|
207
306
|
// so the SDK does it - honoring Retry-After when the server sends one.
|
|
307
|
+
// A 503 can also signal a config boundary (no decision model key); those
|
|
308
|
+
// are not retryable and must surface immediately.
|
|
309
|
+
const nonRetryable503 = new Set(["systemone_unconfigured"]);
|
|
208
310
|
let last;
|
|
209
311
|
for (let attempt = 0; attempt < 5; attempt++) {
|
|
210
312
|
try {
|
|
@@ -216,7 +318,9 @@ export class AgentLu {
|
|
|
216
318
|
}
|
|
217
319
|
catch (e) {
|
|
218
320
|
if (e instanceof AgentLuError &&
|
|
219
|
-
(e.code === "thread_busy" ||
|
|
321
|
+
(e.code === "thread_busy" ||
|
|
322
|
+
e.code === "too_many_active_runs" ||
|
|
323
|
+
(e.status === 503 && !nonRetryable503.has(e.code))) &&
|
|
220
324
|
attempt < 4) {
|
|
221
325
|
last = e;
|
|
222
326
|
await sleep(retryDelayMs(e, attempt));
|
|
@@ -233,8 +337,9 @@ export class AgentLu {
|
|
|
233
337
|
if (body !== undefined && !isForm) {
|
|
234
338
|
headers["Content-Type"] = "application/json";
|
|
235
339
|
}
|
|
236
|
-
|
|
237
|
-
|
|
340
|
+
const apiKey = opts?.apiKey ?? this.apiKey;
|
|
341
|
+
if (opts?.auth !== false && apiKey)
|
|
342
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
238
343
|
const res = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
239
344
|
method,
|
|
240
345
|
headers,
|
|
@@ -273,23 +378,22 @@ export class AgentLu {
|
|
|
273
378
|
* const { data } = await enrich.run({ input: "two eggs and toast" });
|
|
274
379
|
*/
|
|
275
380
|
export class UseCaseRunner {
|
|
276
|
-
parent;
|
|
277
381
|
name;
|
|
278
382
|
registerOpts;
|
|
383
|
+
operations;
|
|
279
384
|
apiKey = null;
|
|
280
385
|
ready = null;
|
|
281
386
|
constructor(parent, name, opts) {
|
|
282
|
-
this.parent = parent;
|
|
283
387
|
this.name = name;
|
|
284
388
|
this.registerOpts = opts ?? {};
|
|
389
|
+
this.operations = scopedUseCaseOperations.get(parent);
|
|
285
390
|
}
|
|
286
391
|
/** Idempotent get-or-create; safe to call before every run. */
|
|
287
392
|
ensure() {
|
|
288
393
|
if (this.apiKey)
|
|
289
394
|
return Promise.resolve();
|
|
290
395
|
if (!this.ready) {
|
|
291
|
-
this.ready = this.
|
|
292
|
-
.useCaseClient(this.name, this.registerOpts)
|
|
396
|
+
this.ready = this.operations.register(this.name, this.registerOpts)
|
|
293
397
|
.then((key) => {
|
|
294
398
|
this.apiKey = key;
|
|
295
399
|
})
|
|
@@ -306,25 +410,29 @@ export class UseCaseRunner {
|
|
|
306
410
|
*/
|
|
307
411
|
async run(opts) {
|
|
308
412
|
await this.ensure();
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
this.parent.apiKey = parentKey;
|
|
316
|
-
}
|
|
413
|
+
return (await this.operations.run(this.apiKey, opts));
|
|
414
|
+
}
|
|
415
|
+
async runJson(opts) {
|
|
416
|
+
assertJsonRunOptions(opts);
|
|
417
|
+
await this.ensure();
|
|
418
|
+
return (await this.operations.runJson(this.apiKey, opts));
|
|
317
419
|
}
|
|
318
420
|
async stream(body) {
|
|
319
421
|
await this.ensure();
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
422
|
+
return this.operations.stream(this.apiKey, body);
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Upload with this use-case's key, so the file is owned by the same
|
|
426
|
+
* service client that will reference it via `run({ fileIds })`.
|
|
427
|
+
*/
|
|
428
|
+
async upload(data, filename, mime) {
|
|
429
|
+
await this.ensure();
|
|
430
|
+
return this.operations.upload(this.apiKey, data, filename, mime);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
function assertJsonRunOptions(opts) {
|
|
434
|
+
if (opts.background === true) {
|
|
435
|
+
throw new AgentLuError(400, "background_not_supported", "runJson requires a completed response and cannot use background.", false);
|
|
328
436
|
}
|
|
329
437
|
}
|
|
330
438
|
function parseRetryAfter(res) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@betterlu/agentlu",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Client for the agentlu service API (/v1). Or just use the openai SDK pointed at http://localhost:8912/v1.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -11,8 +11,12 @@
|
|
|
11
11
|
"import": "./dist/index.js"
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
|
-
"files": [
|
|
15
|
-
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
16
20
|
"scripts": {
|
|
17
21
|
"build": "tsc -p tsconfig.json",
|
|
18
22
|
"prepublishOnly": "npm run build"
|
|
@@ -20,7 +24,13 @@
|
|
|
20
24
|
"publishConfig": {
|
|
21
25
|
"access": "public"
|
|
22
26
|
},
|
|
23
|
-
"keywords": [
|
|
27
|
+
"keywords": [
|
|
28
|
+
"agentlu",
|
|
29
|
+
"openai",
|
|
30
|
+
"responses",
|
|
31
|
+
"llm",
|
|
32
|
+
"agent"
|
|
33
|
+
],
|
|
24
34
|
"repository": {
|
|
25
35
|
"type": "git",
|
|
26
36
|
"url": "git+https://github.com/betterlu/agentlu.git",
|