@midsummer-ai/sdk 0.0.0-stage → 0.1.0-next.1305

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 (79) hide show
  1. package/LICENSE +177 -0
  2. package/README.md +4 -2
  3. package/agents/contract.d.mts +9 -0
  4. package/agents/contract.mjs +221 -0
  5. package/agents/index.d.mts +128 -0
  6. package/agents/index.mjs +203 -0
  7. package/agents/stand-in.d.mts +35 -0
  8. package/agents/stand-in.mjs +149 -0
  9. package/ai/contract.d.mts +10 -0
  10. package/ai/contract.mjs +77 -0
  11. package/ai/index.d.mts +94 -0
  12. package/ai/index.mjs +193 -0
  13. package/ai/stand-in.d.mts +7 -0
  14. package/ai/stand-in.mjs +41 -0
  15. package/build-SQ44jwSa.d.mts +41 -0
  16. package/client.d.mts +45 -0
  17. package/client.mjs +106 -0
  18. package/connection-CxD_Nry5.mjs +1911 -0
  19. package/contract.d.mts +10 -0
  20. package/contract.mjs +469 -0
  21. package/database/build.d.mts +2 -0
  22. package/database/build.mjs +579 -0
  23. package/database/client.d.mts +60 -0
  24. package/database/client.mjs +133 -0
  25. package/database/contract.d.mts +11 -0
  26. package/database/contract.mjs +164 -0
  27. package/database/errors.d.mts +23 -0
  28. package/database/errors.mjs +13 -0
  29. package/database/server.d.mts +3 -0
  30. package/database/server.mjs +320 -0
  31. package/database/stand-in.d.mts +99 -0
  32. package/database/stand-in.mjs +430 -0
  33. package/email/client.d.mts +34 -0
  34. package/email/client.mjs +131 -0
  35. package/email/contract.d.mts +11 -0
  36. package/email/contract.mjs +99 -0
  37. package/email/index.d.mts +74 -0
  38. package/email/index.mjs +59 -0
  39. package/email/smtp.d.mts +22 -0
  40. package/email/smtp.mjs +296 -0
  41. package/email/stand-in.d.mts +13 -0
  42. package/email/stand-in.mjs +51 -0
  43. package/index-DdoK0y7D.d.mts +192 -0
  44. package/index-sgdyoA2P.d.mts +212 -0
  45. package/index.d.mts +2 -0
  46. package/index.mjs +137 -0
  47. package/names-DfCBKupJ.mjs +115 -0
  48. package/package.json +152 -4
  49. package/predict-Drfm_8pO.mjs +179 -0
  50. package/queues/contract.d.mts +13 -0
  51. package/queues/contract.mjs +278 -0
  52. package/queues/index.d.mts +184 -0
  53. package/queues/index.mjs +126 -0
  54. package/queues/stand-in.d.mts +7 -0
  55. package/queues/stand-in.mjs +110 -0
  56. package/queues/wire.d.mts +9 -0
  57. package/queues/wire.mjs +199 -0
  58. package/s3.d.mts +43 -0
  59. package/s3.mjs +1003 -0
  60. package/schema-BF-Y1EVu.mjs +142 -0
  61. package/schema-DS8x1Dn8.mjs +115 -0
  62. package/schema-f7E9uzUn.d.mts +74 -0
  63. package/server-C7O7CICU.d.mts +182 -0
  64. package/socket.d.mts +67 -0
  65. package/socket.mjs +263 -0
  66. package/stand-in.d.mts +7 -0
  67. package/stand-in.mjs +185 -0
  68. package/store-DDuOr-_F.mjs +237 -0
  69. package/tabs-4FLTfg1c.mjs +457 -0
  70. package/vector/contract.d.mts +10 -0
  71. package/vector/contract.mjs +635 -0
  72. package/vector/index.d.mts +2 -0
  73. package/vector/index.mjs +108 -0
  74. package/vector/stand-in.d.mts +11 -0
  75. package/vector/stand-in.mjs +169 -0
  76. package/vector/wire.d.mts +25 -0
  77. package/vector/wire.mjs +175 -0
  78. package/wire.d.mts +54 -0
  79. package/wire.mjs +731 -0
@@ -0,0 +1,203 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ //#region src/agents/index.ts
3
+ /**
4
+ * Agents: what an Agent's one file is, and the runtime that runs it. An Agent's file exports its
5
+ * Agent as default:
6
+ *
7
+ * import type { Agent } from "@midsummer-ai/sdk/agents"
8
+ * export default {
9
+ * tools: { async greet(input: { name: string }) { return `Hello, ${input.name}!` } },
10
+ * async run(input, agent) {
11
+ * const greeting = await agent.tool("greet", { name: input.name })
12
+ * await agent.memory.set("last", input.name)
13
+ * return { greeting }
14
+ * },
15
+ * } satisfies Agent
16
+ *
17
+ * `execute(agent, wire)` runs one Agent run: it begins the run, calls `run` with its input, and
18
+ * finishes it with what `run` returns, or fails it with what `run` throws. Every await on `agent`
19
+ * is a call the wire keeps, numbered in the order the code makes them, before it gets a reply. A
20
+ * run that stopped part way and is executed again makes the same calls and gets the results already
21
+ * recorded, so a completed step is not run again: keep work that should happen once in a step
22
+ * (`agent.step`), and keep `run` itself deterministic outside its calls. A step interrupted before
23
+ * its result is recorded can run again, so make its outside writes idempotent with an identifier
24
+ * that stays the same across attempts. A tool runs only when the version allows it.
25
+ *
26
+ * `httpWire(url, token)` is the wire of a run on the platform; `@midsummer-ai/sdk/agents/stand-in` is
27
+ * one in memory, for running an Agent on this computer.
28
+ *
29
+ * This file imports only what the runtime itself has (`node:async_hooks`): the platform runs it
30
+ * beside an Agent's file, its types taken out, in the Agent version's Worker, with nothing installed.
31
+ * A run reads its Environment's values as `agent.env`.
32
+ */
33
+ var AgentError = class extends Error {
34
+ code;
35
+ constructor(code, message) {
36
+ super(message);
37
+ this.code = code;
38
+ this.name = "AgentError";
39
+ }
40
+ };
41
+ function base64(content) {
42
+ const bytes = typeof content === "string" ? new TextEncoder().encode(content) : content;
43
+ let text = "";
44
+ for (const byte of bytes) text += String.fromCharCode(byte);
45
+ return btoa(text);
46
+ }
47
+ /**
48
+ * Runs one Agent run to its end and returns how it ended. A run that already ended is not run
49
+ * again. When the wire itself fails, so the run can't be kept, it throws, and the run waits to be
50
+ * executed again.
51
+ */
52
+ async function execute(agent, wire, env = {}) {
53
+ const start = await wire.begin();
54
+ if (start.status !== "queued" && start.status !== "running") return {
55
+ status: start.status,
56
+ ...start.output !== void 0 ? { output: start.output } : {},
57
+ ...start.error ? { error: start.error } : {}
58
+ };
59
+ const root = {
60
+ prefix: "",
61
+ next: 0
62
+ };
63
+ const scopes = new AsyncLocalStorage();
64
+ const nextId = () => {
65
+ const scope = scopes.getStore() ?? root;
66
+ return `${scope.prefix}${scope.next++}`;
67
+ };
68
+ const once = async (kind, name, input, work) => {
69
+ const id = nextId();
70
+ const asked = {
71
+ kind,
72
+ name,
73
+ ...input === void 0 ? {} : { input }
74
+ };
75
+ const kept = await wire.call(id, asked);
76
+ if (kept.done) return kept.value;
77
+ const value = await scopes.run({
78
+ prefix: `${id}.`,
79
+ next: 0
80
+ }, work);
81
+ return (await wire.call(id, {
82
+ ...asked,
83
+ done: true,
84
+ value: value === void 0 ? null : value
85
+ })).value;
86
+ };
87
+ const context = {
88
+ runId: start.runId,
89
+ agent: start.agent,
90
+ environment: start.environment,
91
+ version: start.version,
92
+ instructions: start.instructions,
93
+ tools: start.tools,
94
+ env,
95
+ log: async (message, data) => {
96
+ await wire.call(nextId(), {
97
+ kind: "log",
98
+ message: String(message),
99
+ ...data === void 0 ? {} : { data }
100
+ });
101
+ },
102
+ step: (name, work) => once("step", name, void 0, work),
103
+ tool: (name, input) => once("tool", name, input, () => {
104
+ const tool = agent.tools?.[name];
105
+ if (!tool) throw new AgentError("invalid", `The Agent has no tool ${name}.`);
106
+ return tool(input, context);
107
+ }),
108
+ memory: {
109
+ get: (key) => wire.call(nextId(), {
110
+ kind: "memory.get",
111
+ key
112
+ }),
113
+ set: async (key, value) => {
114
+ await wire.call(nextId(), {
115
+ kind: "memory.set",
116
+ key,
117
+ value
118
+ });
119
+ },
120
+ delete: async (key) => {
121
+ await wire.call(nextId(), {
122
+ kind: "memory.delete",
123
+ key
124
+ });
125
+ }
126
+ },
127
+ artifact: async (name, content, type = "text/plain") => {
128
+ await wire.call(nextId(), {
129
+ kind: "artifact",
130
+ name,
131
+ type,
132
+ data: base64(content)
133
+ });
134
+ },
135
+ ask: async (prompt) => await wire.call(nextId(), {
136
+ kind: "ask",
137
+ prompt
138
+ })
139
+ };
140
+ let output;
141
+ try {
142
+ output = await agent.run(start.input, context);
143
+ } catch (error) {
144
+ if (error instanceof AgentError && error.code === "ended") return {
145
+ status: "cancelled",
146
+ error: error.message
147
+ };
148
+ if (error instanceof AgentError && error.code === "unavailable") throw error;
149
+ const message = error instanceof Error ? error.message : String(error);
150
+ await wire.finish({ error: message });
151
+ return {
152
+ status: "failed",
153
+ error: message
154
+ };
155
+ }
156
+ await wire.finish({ output: output === void 0 ? null : output });
157
+ return {
158
+ status: "completed",
159
+ output: output === void 0 ? null : output
160
+ };
161
+ }
162
+ /** The wire of a run on the platform, at `url` with the run's token. */
163
+ function httpWire(url, token, fetcher = fetch) {
164
+ const post = async (body) => {
165
+ let response;
166
+ try {
167
+ response = await fetcher(url, {
168
+ method: "POST",
169
+ headers: {
170
+ Authorization: `Bearer ${token}`,
171
+ "Content-Type": "application/json"
172
+ },
173
+ body: JSON.stringify(body)
174
+ });
175
+ } catch (error) {
176
+ throw new AgentError("unavailable", `The run could not be reached: ${error instanceof Error ? error.message : String(error)}`);
177
+ }
178
+ const answer = await response.json().catch(() => ({}));
179
+ if (!response.ok) throw new AgentError(answer.code ?? (response.status >= 500 ? "unavailable" : "invalid"), answer.error ?? `The run answered ${response.status}.`);
180
+ return answer.answer;
181
+ };
182
+ return {
183
+ begin: () => post({ op: "begin" }),
184
+ call: (id, call) => post({
185
+ op: "call",
186
+ call: id,
187
+ request: call
188
+ }),
189
+ finish: (end) => post({
190
+ op: "finish",
191
+ ...end
192
+ })
193
+ };
194
+ }
195
+ /** Runs the run the environment names, as the platform's host does: `MIDSUMMER_AGENT_WIRE` and `MIDSUMMER_AGENT_TOKEN`; the rest is the run's `env`. */
196
+ async function executeFromEnvironment(agent, env) {
197
+ const url = env.MIDSUMMER_AGENT_WIRE, token = env.MIDSUMMER_AGENT_TOKEN;
198
+ if (!url || !token) throw new Error("MIDSUMMER_AGENT_WIRE and MIDSUMMER_AGENT_TOKEN name the run to execute.");
199
+ const values = Object.fromEntries(Object.entries(env).filter((entry) => entry[1] !== void 0 && !entry[0].startsWith("MIDSUMMER_AGENT_")));
200
+ return execute(agent, httpWire(url, token), values);
201
+ }
202
+ //#endregion
203
+ export { AgentError, execute, executeFromEnvironment, httpWire };
@@ -0,0 +1,35 @@
1
+ import { Agent, RunEnd, RunStatus, RunWire } from "./index.mjs";
2
+ //#region src/agents/stand-in.d.ts
3
+ interface StandInEvent {
4
+ readonly sequence: number;
5
+ readonly type: string;
6
+ readonly call?: string;
7
+ readonly data?: unknown;
8
+ }
9
+ interface AgentStandIn {
10
+ /** A new run's wire, for `execute`, with its events and how it ended so far. */
11
+ start(input?: unknown): {
12
+ readonly wire: RunWire;
13
+ readonly events: ReadonlyArray<StandInEvent>;
14
+ status(): RunStatus;
15
+ };
16
+ /** Runs `agent` once on `input`, to its end. */
17
+ run(agent: Agent, input?: unknown): Promise<{
18
+ readonly end: RunEnd;
19
+ readonly events: ReadonlyArray<StandInEvent>;
20
+ }>;
21
+ /** The memory its runs keep, by key. */
22
+ readonly memory: ReadonlyMap<string, unknown>;
23
+ /** The artifacts its runs kept, by run and name. */
24
+ readonly artifacts: ReadonlyMap<string, {
25
+ readonly type: string;
26
+ readonly data: Uint8Array;
27
+ }>;
28
+ }
29
+ declare function agentStandIn(options?: {
30
+ readonly tools?: ReadonlyArray<string>;
31
+ readonly instructions?: string;
32
+ readonly ask?: (prompt: string) => Promise<string> | string;
33
+ }): AgentStandIn;
34
+ //#endregion
35
+ export { AgentStandIn, StandInEvent, agentStandIn };
@@ -0,0 +1,149 @@
1
+ import { AgentError, execute } from "./index.mjs";
2
+ //#region src/agents/stand-in.ts
3
+ /**
4
+ * Agents' Stand-in: runs in memory, for running an Agent's file on this computer and in tests.
5
+ * It keeps a run's calls as the platform does, so a run executed again resumes, and it refuses
6
+ * tools the run is not allowed. Its memory is kept between the runs one Stand-in starts.
7
+ *
8
+ * import agent from "./agent.ts"
9
+ * import { agentStandIn } from "@midsummer-ai/sdk/agents/stand-in"
10
+ * const local = agentStandIn({ tools: ["greet"] })
11
+ * const { end, events } = await local.run(agent, { name: "Ada" })
12
+ *
13
+ * Model questions are replied to by `ask` when given, else refused as not set up; spend is not kept.
14
+ */
15
+ const clone = (value) => value === void 0 ? value : JSON.parse(JSON.stringify(value));
16
+ function agentStandIn(options = {}) {
17
+ const memory = /* @__PURE__ */ new Map();
18
+ const artifacts = /* @__PURE__ */ new Map();
19
+ let runs = 0;
20
+ function start(input) {
21
+ const runId = `run_${++runs}`;
22
+ const events = [{
23
+ sequence: 0,
24
+ type: "run.queued"
25
+ }];
26
+ let status = "queued";
27
+ let output, error;
28
+ const add = (type, data, call) => events.push({
29
+ sequence: events.length,
30
+ type,
31
+ ...call === void 0 ? {} : { call },
32
+ ...data === void 0 ? {} : { data: clone(data) }
33
+ });
34
+ const keptAt = (id) => events.find((event) => event.call === id);
35
+ const open = () => {
36
+ if (status !== "queued" && status !== "running") throw new AgentError("ended", `The run has ended: ${status}.`);
37
+ };
38
+ return {
39
+ wire: {
40
+ async begin() {
41
+ if (status === "queued") {
42
+ status = "running";
43
+ add("run.started");
44
+ } else if (status === "running") add("run.resumed");
45
+ return {
46
+ runId,
47
+ agent: "agent",
48
+ environment: "development",
49
+ version: 1,
50
+ input: clone(input ?? null),
51
+ instructions: options.instructions ?? "",
52
+ tools: [...options.tools ?? []],
53
+ status,
54
+ ...output !== void 0 ? { output } : {},
55
+ ...error ? { error } : {}
56
+ };
57
+ },
58
+ async call(number, call) {
59
+ const was = keptAt(number);
60
+ if (was) {
61
+ const data = was.data;
62
+ const name = "name" in call ? call.name : "key" in call ? call.key : void 0;
63
+ if (data.kind !== call.kind || (data.name ?? data.key) !== name) throw new AgentError("invalid", `The run's code no longer makes the calls it made: call ${number} was ${data.kind}.`);
64
+ return clone(data.answer);
65
+ }
66
+ open();
67
+ const keep = (type, answer, data) => {
68
+ add(type, {
69
+ kind: call.kind,
70
+ ...data,
71
+ ...answer === void 0 ? {} : { answer }
72
+ }, number);
73
+ return clone(answer);
74
+ };
75
+ switch (call.kind) {
76
+ case "log": return keep("log", void 0, {
77
+ message: call.message,
78
+ ...call.data === void 0 ? {} : { data: call.data }
79
+ });
80
+ case "step":
81
+ case "tool":
82
+ if (call.kind === "tool" && !(options.tools ?? []).includes(call.name)) {
83
+ add("guardrail", { tool: call.name });
84
+ throw new AgentError("refused", `${call.name} is not a tool this version allows.`);
85
+ }
86
+ if (!call.done) return { done: false };
87
+ return keep(call.kind, {
88
+ done: true,
89
+ value: call.value ?? null
90
+ }, { name: call.name });
91
+ case "memory.get": return keep("memory.read", memory.has(call.key) ? memory.get(call.key) : null, { key: call.key });
92
+ case "memory.set":
93
+ memory.set(call.key, clone(call.value));
94
+ return keep("memory.written", null, {
95
+ key: call.key,
96
+ value: call.value
97
+ });
98
+ case "memory.delete":
99
+ memory.delete(call.key);
100
+ return keep("memory.deleted", null, { key: call.key });
101
+ case "artifact": {
102
+ const data = Uint8Array.from(atob(call.data), (char) => char.charCodeAt(0));
103
+ artifacts.set(`${runId}/${call.name}`, {
104
+ type: call.type,
105
+ data
106
+ });
107
+ return keep("artifact", null, {
108
+ name: call.name,
109
+ type: call.type,
110
+ size: data.length
111
+ });
112
+ }
113
+ case "ask":
114
+ if (!options.ask) throw new AgentError("invalid", "Model questions are not set up in this Stand-in: give it `ask`.");
115
+ return keep("model", await options.ask(call.prompt), { prompt: call.prompt });
116
+ }
117
+ },
118
+ async finish(end) {
119
+ if (status !== "queued" && status !== "running") return;
120
+ if ("error" in end) {
121
+ status = "failed";
122
+ error = end.error;
123
+ add("run.failed", { error: end.error });
124
+ } else {
125
+ status = "completed";
126
+ output = clone(end.output);
127
+ add("run.completed", { output: end.output });
128
+ }
129
+ }
130
+ },
131
+ events,
132
+ status: () => status
133
+ };
134
+ }
135
+ return {
136
+ start,
137
+ async run(agent, input) {
138
+ const started = start(input);
139
+ return {
140
+ end: await execute(agent, started.wire),
141
+ events: started.events
142
+ };
143
+ },
144
+ memory,
145
+ artifacts
146
+ };
147
+ }
148
+ //#endregion
149
+ export { agentStandIn };
@@ -0,0 +1,10 @@
1
+ import { Ai } from "./index.mjs";
2
+ //#region src/ai/contract.d.ts
3
+ interface AiContractCase {
4
+ /** A line of the specification. */
5
+ readonly name: string;
6
+ run(ai: Ai): Promise<void>;
7
+ }
8
+ declare const aiContract: ReadonlyArray<AiContractCase>;
9
+ //#endregion
10
+ export { AiContractCase, aiContract };
@@ -0,0 +1,77 @@
1
+ import { AiError } from "./index.mjs";
2
+ //#region src/ai/contract.ts
3
+ /**
4
+ * AI's contract: what every engine promises, as cases any test runner can run. The Stand-in runs
5
+ * them in this package; the platform runs the same cases through the wire, against a provider of
6
+ * its own. A behaviour that is not a case here is not promised.
7
+ *
8
+ * for (const c of aiContract) test(c.name, () => c.run(ai(makeEngine())))
9
+ *
10
+ * Each case gets an `Ai` whose default Model replies, and throws when the engine breaks a promise.
11
+ * Replies differ between Models, so the cases hold their shape, never their words.
12
+ */
13
+ const aiContract = [
14
+ {
15
+ name: "a whole answer has text, the Model that gave it, whole-number tokens and the credits they cost",
16
+ async run(ai) {
17
+ const answer = await ai.complete({ prompt: "Say hello." });
18
+ check(answer.text.length > 0, "the answer has text");
19
+ check(answer.model.length > 0, "the answer names its Model");
20
+ check(Number.isInteger(answer.inputTokens) && answer.inputTokens > 0, "input tokens are a whole number above zero");
21
+ check(Number.isInteger(answer.outputTokens) && answer.outputTokens > 0, "output tokens are a whole number above zero");
22
+ check(answer.credits >= 0 && answer.catalogue.length > 0, "credits are zero or more, under a named catalogue");
23
+ }
24
+ },
25
+ {
26
+ name: "a streamed answer arrives in pieces, and done then settles with what it spent",
27
+ async run(ai) {
28
+ const stream = ai.stream({ messages: [{
29
+ role: "system",
30
+ content: "Be brief."
31
+ }, {
32
+ role: "user",
33
+ content: "Count to three."
34
+ }] });
35
+ const pieces = [];
36
+ for await (const text of stream) pieces.push(text);
37
+ check(pieces.length > 0 && pieces.join("").length > 0, "pieces of text arrived");
38
+ check((await stream.done).outputTokens > 0, "done counts the answer's tokens");
39
+ }
40
+ },
41
+ {
42
+ name: "a Model that is not offered is refused as invalid",
43
+ async run(ai) {
44
+ await refused(() => ai.complete({
45
+ model: "no-such-model-anywhere",
46
+ prompt: "Hi"
47
+ }), "invalid");
48
+ }
49
+ },
50
+ {
51
+ name: "a question that is neither a prompt nor messages, or a conversation not ending with a user message, is refused as invalid",
52
+ async run(ai) {
53
+ await refused(() => ai.complete({}), "invalid");
54
+ await refused(() => ai.complete({ messages: [{
55
+ role: "user",
56
+ content: "Hi"
57
+ }, {
58
+ role: "assistant",
59
+ content: "Hello"
60
+ }] }), "invalid");
61
+ }
62
+ }
63
+ ];
64
+ function check(holds, what) {
65
+ if (!holds) throw new Error(`Broken promise: ${what}.`);
66
+ }
67
+ async function refused(call, code) {
68
+ try {
69
+ await call();
70
+ } catch (error) {
71
+ if (error instanceof AiError && error.code === code) return;
72
+ throw new Error(`Expected an AiError ${code}, got ${error instanceof AiError ? error.code : String(error)}.`);
73
+ }
74
+ throw new Error(`Expected an AiError ${code}, but the call succeeded.`);
75
+ }
76
+ //#endregion
77
+ export { aiContract };
package/ai/index.d.mts ADDED
@@ -0,0 +1,94 @@
1
+ //#region src/ai/index.d.ts
2
+ /**
3
+ * AI: Model calls from a Project's code, through one AI key, streamed or whole, each with the tokens
4
+ * and credits it spent.
5
+ *
6
+ * const models = ai(aiClient("https://api.midsummer.new", { key: process.env.MIDSUMMER_AI_KEY! }))
7
+ * const answer = await models.complete({ prompt: "Name three rivers" })
8
+ * const stream = models.stream({ model: "claude-sonnet-5-5", messages: [{ role: "user", content: "Hi" }] })
9
+ * for await (const text of stream) write(text)
10
+ * const { credits } = await stream.done
11
+ *
12
+ * The engine is where calls go: the Stand-in (`@midsummer-ai/sdk/ai/stand-in`) for `midsummer dev`,
13
+ * or the platform (`aiClient`). Every engine is held to the cases in `@midsummer-ai/sdk/ai/contract`.
14
+ * A Model is named as `midsummer ai model list` lists it; leaving it out picks the default.
15
+ *
16
+ * Every failure is an `AiError`; its `code` tells you which kind: `invalid` (no such Model, nothing
17
+ * asked), `forbidden` (the key is not valid), `quota` (the Team's usage is holding back its calls),
18
+ * `conflict` (a retry by `idempotencyKey` of a call still under way) or `unavailable` (try again).
19
+ */
20
+ type AiErrorCode = "invalid" | "forbidden" | "quota" | "conflict" | "unavailable";
21
+ declare class AiError extends Error {
22
+ readonly code: AiErrorCode;
23
+ constructor(code: AiErrorCode, why: string);
24
+ }
25
+ interface Message {
26
+ readonly role: "system" | "user" | "assistant";
27
+ readonly content: string;
28
+ }
29
+ /** What to ask: a `prompt` (after an optional `system` prompt) or a conversation of `messages`. */
30
+ interface Ask {
31
+ readonly model?: string;
32
+ readonly prompt?: string;
33
+ readonly system?: string;
34
+ readonly messages?: ReadonlyArray<Message>;
35
+ /** The most tokens the answer may have: 1 to 32,000, 1,024 unless given. */
36
+ readonly maxTokens?: number;
37
+ /** 0 to 2. */
38
+ readonly temperature?: number;
39
+ /**
40
+ * Names this call for a day. A retry with it after a lost answer gets the first call's answer and
41
+ * is not charged again; while the first still runs, it fails as `conflict`.
42
+ */
43
+ readonly idempotencyKey?: string;
44
+ }
45
+ /** What a call spent: its tokens, and their price in credits under the catalogue version `catalogue`. */
46
+ interface Spent {
47
+ readonly model: string;
48
+ readonly inputTokens: number;
49
+ readonly outputTokens: number;
50
+ readonly credits: number;
51
+ readonly catalogue: string;
52
+ }
53
+ /** A piece of the answer, or, last, what the call spent. */
54
+ type AiEvent = {
55
+ readonly text: string;
56
+ } | {
57
+ readonly done: Spent;
58
+ };
59
+ /** Where calls go. `ask` streams one call's events and throws `AiError`. */
60
+ interface AiEngine {
61
+ ask(input: Ask): AsyncIterable<AiEvent>;
62
+ }
63
+ interface Answer extends Spent {
64
+ readonly text: string;
65
+ }
66
+ /** An answer's text as it arrives; `done` settles with what it spent once it has all arrived. */
67
+ interface AnswerStream extends AsyncIterable<string> {
68
+ readonly done: Promise<Spent>;
69
+ }
70
+ interface Ai {
71
+ complete(input: Ask): Promise<Answer>;
72
+ stream(input: Ask): AnswerStream;
73
+ }
74
+ declare function ai(engine: AiEngine): Ai;
75
+ type Fetch = (url: string, init: RequestInit) => Promise<Response>;
76
+ /**
77
+ * The platform's AI Gateway at `origin` (the API's), for the AI key `key`: each call is one POST to
78
+ * `<origin>/v1/ai/ask`, which replies with newline-delimited JSON events as the Model writes.
79
+ */
80
+ declare function aiClient(origin: string, options: {
81
+ readonly key: string;
82
+ readonly fetch?: Fetch;
83
+ }): AiEngine;
84
+ /**
85
+ * What Midsummer hands a Deployment as `context.ai`: the Team's AI Gateway, with no key. Each call
86
+ * streams the events `aiClient` reads.
87
+ *
88
+ * const answer = await ai(boundAi(context.ai)).complete({ prompt: "Hello" })
89
+ */
90
+ declare function boundAi(handle: {
91
+ ask(input: Ask): Promise<ReadableStream<Uint8Array>>;
92
+ }): AiEngine;
93
+ //#endregion
94
+ export { Ai, AiEngine, AiError, AiErrorCode, AiEvent, Answer, AnswerStream, Ask, Message, Spent, ai, aiClient, boundAi };