@awesomate/platform-sdk 0.1.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 +92 -0
- package/dist/cjs/client.d.ts +128 -0
- package/dist/cjs/client.js +257 -0
- package/dist/cjs/client.js.map +1 -0
- package/dist/cjs/http.d.ts +27 -0
- package/dist/cjs/http.js +127 -0
- package/dist/cjs/http.js.map +1 -0
- package/dist/cjs/index.d.ts +9 -0
- package/dist/cjs/index.js +35 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/service.d.ts +140 -0
- package/dist/cjs/service.js +216 -0
- package/dist/cjs/service.js.map +1 -0
- package/dist/cjs/sign.d.ts +11 -0
- package/dist/cjs/sign.js +41 -0
- package/dist/cjs/sign.js.map +1 -0
- package/dist/cjs/sse.d.ts +27 -0
- package/dist/cjs/sse.js +130 -0
- package/dist/cjs/sse.js.map +1 -0
- package/dist/cjs/types.d.ts +517 -0
- package/dist/cjs/types.js +4 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/client.d.ts +128 -0
- package/dist/esm/client.js +251 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/http.d.ts +27 -0
- package/dist/esm/http.js +116 -0
- package/dist/esm/http.js.map +1 -0
- package/dist/esm/index.d.ts +9 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/service.d.ts +140 -0
- package/dist/esm/service.js +210 -0
- package/dist/esm/service.js.map +1 -0
- package/dist/esm/sign.d.ts +11 -0
- package/dist/esm/sign.js +36 -0
- package/dist/esm/sign.js.map +1 -0
- package/dist/esm/sse.d.ts +27 -0
- package/dist/esm/sse.js +125 -0
- package/dist/esm/sse.js.map +1 -0
- package/dist/esm/types.d.ts +517 -0
- package/dist/esm/types.js +3 -0
- package/dist/esm/types.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# @awesomate/platform-sdk
|
|
2
|
+
|
|
3
|
+
Typed client for the Awesomate platform `/v1` API ([docs/api-v1.md](../../docs/api-v1.md)).
|
|
4
|
+
Zero dependencies; runs in browsers and Node ≥ 20 (ESM and CommonJS builds). Apps talk to
|
|
5
|
+
the platform only through this — never through `@platform/*` packages, Postgres, Meilisearch
|
|
6
|
+
or Ollama ([docs/design/app-layer-separation.md](../../docs/design/app-layer-separation.md)).
|
|
7
|
+
|
|
8
|
+
Two clients, one error contract:
|
|
9
|
+
|
|
10
|
+
| Client | Auth | Who uses it |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| `PlatformClient` | a tenant key — `amk_q_` (server), `amk_p_` (browser, origin-locked), `amk_i_` (ingest), `amk_u_` (user token) | apps, n8n, widgets, the MCP server |
|
|
13
|
+
| `ServiceClient` | `amk_s_` key + HMAC (`X-Awm-*` headers) | the hub only |
|
|
14
|
+
|
|
15
|
+
Every call resolves to a `PlatformResult<T>` and never throws on the request path:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
type PlatformResult<T> =
|
|
19
|
+
| { ok: true; status: number; data: T }
|
|
20
|
+
| { ok: false; status: number; error: string; message?: string; requestId?: string; retryAfter?: string } // 4xx envelope
|
|
21
|
+
| { ok: false; reason: "unavailable" }; // 5xx, timeout, network, no config
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Tenant API
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { PlatformClient } from "@awesomate/platform-sdk";
|
|
28
|
+
|
|
29
|
+
const platform = new PlatformClient({ baseUrl: "https://knowledge.awesomate.ai", apiKey: process.env.AMK_KEY! });
|
|
30
|
+
|
|
31
|
+
// Validated answer, JSON
|
|
32
|
+
const r = await platform.answer({ question: "What does chapter 3 say about pricing?", session: "user-42", agent: "knowledge" });
|
|
33
|
+
if (r.ok) console.log(r.data.answer_plain, r.data.sources);
|
|
34
|
+
|
|
35
|
+
// Streamed: sources first, then sentences that already passed the citation gate, then meta
|
|
36
|
+
const s = await platform.answerStream({ question: "…", session: "user-42" });
|
|
37
|
+
if (s.ok) {
|
|
38
|
+
for await (const e of s.events()) {
|
|
39
|
+
if (e.event === "answer") process.stdout.write(e.text);
|
|
40
|
+
else if (e.event === "meta") console.log("\n", e.meta.status, e.meta.repaired);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// …or pipe `s.body` (the raw text/event-stream) straight to your own response.
|
|
44
|
+
|
|
45
|
+
// Raw passages, identity, feedback
|
|
46
|
+
await platform.query({ q: "webinar funnels", dataset: "social", limit: 5 });
|
|
47
|
+
await platform.whoami();
|
|
48
|
+
await platform.feedback({ session: "user-42", ref: 1, rating: "up" });
|
|
49
|
+
|
|
50
|
+
// Business data
|
|
51
|
+
await platform.queryData({ dataset: "ds-pnl", measures: [{ column: "c_amount", agg: "sum" }], time: { preset: "fy_to_date" } });
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Ingestion with an `amk_i_` key: `createUpload → uploadBytes(put_url, bytes) → completeUpload →
|
|
55
|
+
createJob({kind, upload_id}, {idempotencyKey})`, then `getJob` / `listJobs` / `cancelJob` / `retryJob`.
|
|
56
|
+
|
|
57
|
+
In a browser, use an `amk_p_` key minted with `allowed_origins`; the platform refuses it from any
|
|
58
|
+
other Origin. Never ship a `q` or `i` key to a browser.
|
|
59
|
+
|
|
60
|
+
## Service channel (hub)
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { ServiceClient, countTenantSources } from "@awesomate/platform-sdk";
|
|
64
|
+
|
|
65
|
+
const admin = new ServiceClient({
|
|
66
|
+
adminUrl: process.env.KNOWLEDGE_PLATFORM_ADMIN_URL!,
|
|
67
|
+
serviceKey: process.env.KNOWLEDGE_PLATFORM_SERVICE_KEY!,
|
|
68
|
+
hmacSecret: process.env.KNOWLEDGE_PLATFORM_HMAC_SECRET!,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await admin.upsertTenant("dale", { contact_id: 42, display_name: "Dale", plan: "pro" }, `kt:dale:${version}`);
|
|
72
|
+
const minted = await admin.mintKey("dale", { class: "p", name: "social console", allowed_origins: ["https://social.awesomate.ai"] }, "key:dale:console:1");
|
|
73
|
+
// minted.data.key is returned exactly once — hand it to its destination and discard.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Every mutation takes an idempotency key (≤ 96 chars); replays return the platform's stored
|
|
77
|
+
response. Reads carry none. `uploadBytes` PUTs to the platform's `put_url` verbatim, with no
|
|
78
|
+
service headers — the URL is the credential.
|
|
79
|
+
|
|
80
|
+
## Node streams
|
|
81
|
+
|
|
82
|
+
The SDK returns web `ReadableStream`s. In Node/Express:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { Readable } from "node:stream";
|
|
86
|
+
if (s.ok) Readable.fromWeb(s.body as import("node:stream/web").ReadableStream).pipe(res);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Build
|
|
90
|
+
|
|
91
|
+
`pnpm --filter @awesomate/platform-sdk build` emits `dist/esm` and `dist/cjs`; `test` runs vitest.
|
|
92
|
+
Version with the API it wraps: additive `/v1` changes bump the minor, `/v2` starts `1.0`.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { FetchLike } from "./http.js";
|
|
2
|
+
import type { CollectedAnswer } from "./sse.js";
|
|
3
|
+
import type { Answer, AnswerRequest, AnswerStreamEvent, CompleteUploadBody, CreateImportBody, CreateJobBody, CreateUploadBody, DataQuery, DataQueryResult, DatasetSummary, Enqueued, Feedback, IngestJob, JobsPage, KeyClass, PlatformResult, QueryParams, QueryResponse, Status, UploadTicket, Whoami } from "./types.js";
|
|
4
|
+
export interface PlatformClientConfig {
|
|
5
|
+
/** Tenant API base, e.g. `https://knowledge.awesomate.ai` (dev: `https://api.dev.awesomate.dev`). */
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
/** A tenant key: `amk_q_` (server), `amk_p_` (browser, origin-locked), `amk_i_` (ingest), `amk_u_` (user token). */
|
|
8
|
+
apiKey: string;
|
|
9
|
+
/** Control-plane budget; answers and uploads have their own. Default 10 s. */
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
/** Long-running answer budget. Default 5 min. */
|
|
12
|
+
answerTimeoutMs?: number;
|
|
13
|
+
/** Byte-upload budget. Default 10 min. */
|
|
14
|
+
uploadTimeoutMs?: number;
|
|
15
|
+
fetchImpl?: FetchLike;
|
|
16
|
+
}
|
|
17
|
+
export type AnswerStreamResult = {
|
|
18
|
+
ok: true;
|
|
19
|
+
status: number;
|
|
20
|
+
contentType: string;
|
|
21
|
+
/** The raw SSE body — pipe it straight through to your own response. */
|
|
22
|
+
body: ReadableStream<Uint8Array>;
|
|
23
|
+
/** Typed events over the same body; consume either `body` or `events()`, not both. */
|
|
24
|
+
events: () => AsyncGenerator<AnswerStreamEvent>;
|
|
25
|
+
/** Drain to the final text + meta. */
|
|
26
|
+
collect: () => Promise<CollectedAnswer>;
|
|
27
|
+
} | Exclude<PlatformResult<never>, {
|
|
28
|
+
ok: true;
|
|
29
|
+
}>;
|
|
30
|
+
/** Class of a platform key, or null when the string is not a well-formed key. */
|
|
31
|
+
export declare function keyClass(key: string): KeyClass | null;
|
|
32
|
+
/**
|
|
33
|
+
* Client for the tenant API (`/v1/*`). One instance = one key = one tenant; the platform
|
|
34
|
+
* scopes every call server-side. Never throws on the request path — see PlatformResult.
|
|
35
|
+
*/
|
|
36
|
+
export declare class PlatformClient {
|
|
37
|
+
private readonly baseUrl;
|
|
38
|
+
private readonly apiKey;
|
|
39
|
+
private readonly timeoutMs;
|
|
40
|
+
private readonly answerTimeoutMs;
|
|
41
|
+
private readonly uploadTimeoutMs;
|
|
42
|
+
private readonly fetchImpl;
|
|
43
|
+
constructor(config: PlatformClientConfig);
|
|
44
|
+
isConfigured(): boolean;
|
|
45
|
+
private headers;
|
|
46
|
+
private request;
|
|
47
|
+
/** `POST /v1/answer`, JSON: the validated answer with numbered sources. */
|
|
48
|
+
answer(body: AnswerRequest, opts?: {
|
|
49
|
+
signal?: AbortSignal;
|
|
50
|
+
}): Promise<PlatformResult<Answer>>;
|
|
51
|
+
/** `POST /v1/answer` with `Accept: text/event-stream`: sources first, then gated sentences, then meta. */
|
|
52
|
+
answerStream(body: AnswerRequest, opts?: {
|
|
53
|
+
signal?: AbortSignal;
|
|
54
|
+
}): Promise<AnswerStreamResult>;
|
|
55
|
+
/** v2 `POST /v1/agents/:agent_id/chat` streamed (Phase 1; `404 not_found` until it lands). */
|
|
56
|
+
agentChatStream(agentId: string, body: {
|
|
57
|
+
message: string;
|
|
58
|
+
session_id?: string;
|
|
59
|
+
include_media?: boolean;
|
|
60
|
+
metadata?: Record<string, unknown>;
|
|
61
|
+
}, opts?: {
|
|
62
|
+
signal?: AbortSignal;
|
|
63
|
+
}): Promise<AnswerStreamResult>;
|
|
64
|
+
private stream;
|
|
65
|
+
/** `GET /v1/query` — raw hybrid-search passages (bypass the citation gate). */
|
|
66
|
+
query(params: QueryParams, opts?: {
|
|
67
|
+
signal?: AbortSignal;
|
|
68
|
+
}): Promise<PlatformResult<QueryResponse>>;
|
|
69
|
+
whoami(): Promise<PlatformResult<Whoami>>;
|
|
70
|
+
status(): Promise<PlatformResult<Status>>;
|
|
71
|
+
feedback(body: Feedback): Promise<PlatformResult<void>>;
|
|
72
|
+
/**
|
|
73
|
+
* `GET /v1/media/<key>`: the platform 302s to a 10-minute presigned URL. `fetch` follows
|
|
74
|
+
* it, so the Response carries the bytes. Prefer presigned URLs from answer sources where
|
|
75
|
+
* the platform supplies them; use this when you hold only a media key.
|
|
76
|
+
*/
|
|
77
|
+
media(key: string, init?: {
|
|
78
|
+
range?: string;
|
|
79
|
+
signal?: AbortSignal;
|
|
80
|
+
}): Promise<PlatformResult<Response>>;
|
|
81
|
+
createUpload(body: CreateUploadBody): Promise<PlatformResult<UploadTicket>>;
|
|
82
|
+
completeUpload(uploadId: string, body?: CompleteUploadBody): Promise<PlatformResult<{
|
|
83
|
+
upload_id: string;
|
|
84
|
+
status: string;
|
|
85
|
+
}>>;
|
|
86
|
+
/**
|
|
87
|
+
* PUT the staged bytes to `put_url` EXACTLY as the platform returned it. A presigned S3 URL
|
|
88
|
+
* is its own credential and gets no headers of ours. On FsStore deployments the platform
|
|
89
|
+
* hands back its own `/v1/ingest/uploads/:id/content` route instead, which sits behind the
|
|
90
|
+
* same ingest-key auth as every `/v1/ingest/*` call, so that one carries the Bearer key.
|
|
91
|
+
* A relative `put_url` resolves against `baseUrl`.
|
|
92
|
+
*/
|
|
93
|
+
uploadBytes(putUrl: string, body: BodyInit | ReadableStream<Uint8Array>, sizeBytes?: number): Promise<PlatformResult<void>>;
|
|
94
|
+
/** `POST /v1/ingest/jobs`; pass `idempotencyKey` so a retry returns the same job. */
|
|
95
|
+
createJob(body: CreateJobBody, opts?: {
|
|
96
|
+
idempotencyKey?: string;
|
|
97
|
+
}): Promise<PlatformResult<Enqueued>>;
|
|
98
|
+
getJob(jobId: string): Promise<PlatformResult<IngestJob>>;
|
|
99
|
+
listJobs(params?: {
|
|
100
|
+
status?: string;
|
|
101
|
+
limit?: number;
|
|
102
|
+
cursor?: string;
|
|
103
|
+
}): Promise<PlatformResult<JobsPage>>;
|
|
104
|
+
cancelJob(jobId: string): Promise<PlatformResult<{
|
|
105
|
+
job_id: string;
|
|
106
|
+
status: string;
|
|
107
|
+
}>>;
|
|
108
|
+
retryJob(jobId: string): Promise<PlatformResult<Enqueued>>;
|
|
109
|
+
listDatasets(): Promise<PlatformResult<{
|
|
110
|
+
datasets: DatasetSummary[];
|
|
111
|
+
}>>;
|
|
112
|
+
getDataset(datasetId: string): Promise<PlatformResult<DatasetSummary & Record<string, unknown>>>;
|
|
113
|
+
queryData(dsl: DataQuery, opts?: {
|
|
114
|
+
signal?: AbortSignal;
|
|
115
|
+
}): Promise<PlatformResult<DataQueryResult>>;
|
|
116
|
+
createImport(body: CreateImportBody, opts?: {
|
|
117
|
+
idempotencyKey?: string;
|
|
118
|
+
}): Promise<PlatformResult<Enqueued & {
|
|
119
|
+
import_id: string | null;
|
|
120
|
+
}>>;
|
|
121
|
+
getImport(id: string): Promise<PlatformResult<Record<string, unknown>>>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Shared by the tenant and service clients: a PUT of raw bytes to the platform's `put_url`.
|
|
125
|
+
* `headers` is empty for presigned URLs (the URL is the credential) and carries the Bearer
|
|
126
|
+
* key only for the platform's own auth-gated upload route.
|
|
127
|
+
*/
|
|
128
|
+
export declare function uploadTo(fetchImpl: FetchLike, url: string, body: BodyInit | ReadableStream<Uint8Array>, sizeBytes: number | undefined, timeoutMs: number, headers?: Record<string, string>): Promise<PlatformResult<void>>;
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PlatformClient = void 0;
|
|
4
|
+
exports.keyClass = keyClass;
|
|
5
|
+
exports.uploadTo = uploadTo;
|
|
6
|
+
const http_js_1 = require("./http.js");
|
|
7
|
+
const sse_js_1 = require("./sse.js");
|
|
8
|
+
const KEY_RE = /^amk_([qpiasu])_[0-9A-Za-z]{40}$/;
|
|
9
|
+
/** Class of a platform key, or null when the string is not a well-formed key. */
|
|
10
|
+
function keyClass(key) {
|
|
11
|
+
const m = KEY_RE.exec(key);
|
|
12
|
+
return m ? m[1] : null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Client for the tenant API (`/v1/*`). One instance = one key = one tenant; the platform
|
|
16
|
+
* scopes every call server-side. Never throws on the request path — see PlatformResult.
|
|
17
|
+
*/
|
|
18
|
+
class PlatformClient {
|
|
19
|
+
baseUrl;
|
|
20
|
+
apiKey;
|
|
21
|
+
timeoutMs;
|
|
22
|
+
answerTimeoutMs;
|
|
23
|
+
uploadTimeoutMs;
|
|
24
|
+
fetchImpl;
|
|
25
|
+
constructor(config) {
|
|
26
|
+
this.baseUrl = (0, http_js_1.trimSlash)(config.baseUrl ?? "");
|
|
27
|
+
this.apiKey = config.apiKey ?? "";
|
|
28
|
+
this.timeoutMs = config.timeoutMs ?? 10_000;
|
|
29
|
+
this.answerTimeoutMs = config.answerTimeoutMs ?? 5 * 60_000;
|
|
30
|
+
this.uploadTimeoutMs = config.uploadTimeoutMs ?? 10 * 60_000;
|
|
31
|
+
this.fetchImpl = config.fetchImpl ?? (0, http_js_1.defaultFetch)();
|
|
32
|
+
}
|
|
33
|
+
isConfigured() {
|
|
34
|
+
return Boolean(this.baseUrl && this.apiKey);
|
|
35
|
+
}
|
|
36
|
+
headers(extra = {}) {
|
|
37
|
+
return { Authorization: `Bearer ${this.apiKey}`, ...extra };
|
|
38
|
+
}
|
|
39
|
+
async request(method, path, opts = {}) {
|
|
40
|
+
if (!this.isConfigured())
|
|
41
|
+
return http_js_1.UNAVAILABLE;
|
|
42
|
+
const headers = this.headers(opts.headers);
|
|
43
|
+
const rawBody = opts.body === undefined ? undefined : JSON.stringify(opts.body);
|
|
44
|
+
if (rawBody !== undefined)
|
|
45
|
+
headers["Content-Type"] = "application/json";
|
|
46
|
+
const d = (0, http_js_1.deadline)(opts.timeoutMs ?? this.timeoutMs, opts.signal);
|
|
47
|
+
try {
|
|
48
|
+
const res = await this.fetchImpl(`${this.baseUrl}${path}`, { method, headers, body: rawBody, signal: d.signal });
|
|
49
|
+
return await (0, http_js_1.resultOf)(res);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return http_js_1.UNAVAILABLE;
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
d.done();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// --- answers ---------------------------------------------------------------------------
|
|
59
|
+
/** `POST /v1/answer`, JSON: the validated answer with numbered sources. */
|
|
60
|
+
answer(body, opts = {}) {
|
|
61
|
+
return this.request("POST", "/v1/answer", {
|
|
62
|
+
body,
|
|
63
|
+
headers: { Accept: "application/json" },
|
|
64
|
+
signal: opts.signal,
|
|
65
|
+
timeoutMs: this.answerTimeoutMs,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** `POST /v1/answer` with `Accept: text/event-stream`: sources first, then gated sentences, then meta. */
|
|
69
|
+
async answerStream(body, opts = {}) {
|
|
70
|
+
return this.stream("/v1/answer", body, opts.signal);
|
|
71
|
+
}
|
|
72
|
+
/** v2 `POST /v1/agents/:agent_id/chat` streamed (Phase 1; `404 not_found` until it lands). */
|
|
73
|
+
async agentChatStream(agentId, body, opts = {}) {
|
|
74
|
+
return this.stream(`/v1/agents/${(0, http_js_1.enc)(agentId)}/chat`, body, opts.signal);
|
|
75
|
+
}
|
|
76
|
+
async stream(path, body, signal) {
|
|
77
|
+
if (!this.isConfigured())
|
|
78
|
+
return http_js_1.UNAVAILABLE;
|
|
79
|
+
// The budget covers the headers (time to first byte). Once the stream is open the caller's
|
|
80
|
+
// own signal governs it, so the timer stops at headers while the abort link stays wired
|
|
81
|
+
// until the body ends or is cancelled.
|
|
82
|
+
const d = (0, http_js_1.deadline)(this.answerTimeoutMs, signal);
|
|
83
|
+
let res;
|
|
84
|
+
try {
|
|
85
|
+
res = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
headers: this.headers({ "Content-Type": "application/json", Accept: "text/event-stream" }),
|
|
88
|
+
body: JSON.stringify(body),
|
|
89
|
+
signal: d.signal,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
d.done();
|
|
94
|
+
return http_js_1.UNAVAILABLE;
|
|
95
|
+
}
|
|
96
|
+
if (!res.ok) {
|
|
97
|
+
d.done();
|
|
98
|
+
return (0, http_js_1.failureOf)(res);
|
|
99
|
+
}
|
|
100
|
+
d.clearTimer();
|
|
101
|
+
const stream = res.body ? (0, http_js_1.untilEnd)(res.body, d.done) : emptyBody(d.done);
|
|
102
|
+
return {
|
|
103
|
+
ok: true,
|
|
104
|
+
status: res.status,
|
|
105
|
+
contentType: res.headers?.get?.("content-type") ?? "text/event-stream",
|
|
106
|
+
body: stream,
|
|
107
|
+
events: () => (0, sse_js_1.answerEvents)(stream),
|
|
108
|
+
collect: () => (0, sse_js_1.collectAnswer)(stream),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// --- search / identity -----------------------------------------------------------------
|
|
112
|
+
/** `GET /v1/query` — raw hybrid-search passages (bypass the citation gate). */
|
|
113
|
+
query(params, opts = {}) {
|
|
114
|
+
return this.request("GET", `/v1/query${(0, http_js_1.queryString)(params)}`, { signal: opts.signal });
|
|
115
|
+
}
|
|
116
|
+
whoami() {
|
|
117
|
+
return this.request("GET", "/v1/whoami");
|
|
118
|
+
}
|
|
119
|
+
status() {
|
|
120
|
+
return this.request("GET", "/v1/status");
|
|
121
|
+
}
|
|
122
|
+
feedback(body) {
|
|
123
|
+
return this.request("POST", "/v1/feedback", { body });
|
|
124
|
+
}
|
|
125
|
+
// --- media -----------------------------------------------------------------------------
|
|
126
|
+
/**
|
|
127
|
+
* `GET /v1/media/<key>`: the platform 302s to a 10-minute presigned URL. `fetch` follows
|
|
128
|
+
* it, so the Response carries the bytes. Prefer presigned URLs from answer sources where
|
|
129
|
+
* the platform supplies them; use this when you hold only a media key.
|
|
130
|
+
*/
|
|
131
|
+
async media(key, init = {}) {
|
|
132
|
+
if (!this.isConfigured())
|
|
133
|
+
return http_js_1.UNAVAILABLE;
|
|
134
|
+
const path = key
|
|
135
|
+
.replace(/^\/+/, "")
|
|
136
|
+
.split("/")
|
|
137
|
+
.map(http_js_1.enc)
|
|
138
|
+
.join("/");
|
|
139
|
+
const d = (0, http_js_1.deadline)(this.uploadTimeoutMs, init.signal);
|
|
140
|
+
let res;
|
|
141
|
+
try {
|
|
142
|
+
res = await this.fetchImpl(`${this.baseUrl}/v1/media/${path}`, {
|
|
143
|
+
method: "GET",
|
|
144
|
+
headers: this.headers(init.range ? { Range: init.range } : {}),
|
|
145
|
+
redirect: "follow",
|
|
146
|
+
signal: d.signal,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
d.done();
|
|
151
|
+
return http_js_1.UNAVAILABLE;
|
|
152
|
+
}
|
|
153
|
+
if (!res.ok) {
|
|
154
|
+
d.done();
|
|
155
|
+
return (0, http_js_1.failureOf)(res);
|
|
156
|
+
}
|
|
157
|
+
if (!res.body) {
|
|
158
|
+
d.done();
|
|
159
|
+
return { ok: true, status: res.status, data: res };
|
|
160
|
+
}
|
|
161
|
+
// Same shape as the platform's Response, with the body wrapped so the caller's abort
|
|
162
|
+
// stays wired while bytes flow and the link is dropped when they stop.
|
|
163
|
+
d.clearTimer();
|
|
164
|
+
return { ok: true, status: res.status, data: new Response((0, http_js_1.untilEnd)(res.body, d.done), res) };
|
|
165
|
+
}
|
|
166
|
+
// --- ingestion (amk_i_ keys) -----------------------------------------------------------
|
|
167
|
+
createUpload(body) {
|
|
168
|
+
return this.request("POST", "/v1/ingest/uploads", { body });
|
|
169
|
+
}
|
|
170
|
+
completeUpload(uploadId, body = {}) {
|
|
171
|
+
return this.request("POST", `/v1/ingest/uploads/${(0, http_js_1.enc)(uploadId)}/complete`, { body });
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* PUT the staged bytes to `put_url` EXACTLY as the platform returned it. A presigned S3 URL
|
|
175
|
+
* is its own credential and gets no headers of ours. On FsStore deployments the platform
|
|
176
|
+
* hands back its own `/v1/ingest/uploads/:id/content` route instead, which sits behind the
|
|
177
|
+
* same ingest-key auth as every `/v1/ingest/*` call, so that one carries the Bearer key.
|
|
178
|
+
* A relative `put_url` resolves against `baseUrl`.
|
|
179
|
+
*/
|
|
180
|
+
async uploadBytes(putUrl, body, sizeBytes) {
|
|
181
|
+
const url = putUrl.startsWith("/") ? `${this.baseUrl}${putUrl}` : putUrl;
|
|
182
|
+
const platformRoute = url.startsWith(`${this.baseUrl}/v1/ingest/`);
|
|
183
|
+
return uploadTo(this.fetchImpl, url, body, sizeBytes, this.uploadTimeoutMs, platformRoute ? this.headers() : {});
|
|
184
|
+
}
|
|
185
|
+
/** `POST /v1/ingest/jobs`; pass `idempotencyKey` so a retry returns the same job. */
|
|
186
|
+
createJob(body, opts = {}) {
|
|
187
|
+
return this.request("POST", "/v1/ingest/jobs", {
|
|
188
|
+
body,
|
|
189
|
+
headers: opts.idempotencyKey ? { "Idempotency-Key": opts.idempotencyKey } : {},
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
getJob(jobId) {
|
|
193
|
+
return this.request("GET", `/v1/ingest/jobs/${(0, http_js_1.enc)(jobId)}`);
|
|
194
|
+
}
|
|
195
|
+
listJobs(params = {}) {
|
|
196
|
+
return this.request("GET", `/v1/ingest/jobs${(0, http_js_1.queryString)(params)}`);
|
|
197
|
+
}
|
|
198
|
+
cancelJob(jobId) {
|
|
199
|
+
return this.request("POST", `/v1/ingest/jobs/${(0, http_js_1.enc)(jobId)}/cancel`, { body: {} });
|
|
200
|
+
}
|
|
201
|
+
retryJob(jobId) {
|
|
202
|
+
return this.request("POST", `/v1/ingest/jobs/${(0, http_js_1.enc)(jobId)}/retry`, { body: {} });
|
|
203
|
+
}
|
|
204
|
+
// --- business data ---------------------------------------------------------------------
|
|
205
|
+
listDatasets() {
|
|
206
|
+
return this.request("GET", "/v1/data/datasets");
|
|
207
|
+
}
|
|
208
|
+
getDataset(datasetId) {
|
|
209
|
+
return this.request("GET", `/v1/data/datasets/${(0, http_js_1.enc)(datasetId)}`);
|
|
210
|
+
}
|
|
211
|
+
queryData(dsl, opts = {}) {
|
|
212
|
+
return this.request("POST", "/v1/data/query", { body: dsl, signal: opts.signal });
|
|
213
|
+
}
|
|
214
|
+
createImport(body, opts = {}) {
|
|
215
|
+
return this.request("POST", "/v1/data/imports", {
|
|
216
|
+
body,
|
|
217
|
+
headers: opts.idempotencyKey ? { "Idempotency-Key": opts.idempotencyKey } : {},
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
getImport(id) {
|
|
221
|
+
return this.request("GET", `/v1/data/imports/${(0, http_js_1.enc)(id)}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
exports.PlatformClient = PlatformClient;
|
|
225
|
+
const emptyBody = (onEnd) => new ReadableStream({
|
|
226
|
+
start(c) {
|
|
227
|
+
onEnd();
|
|
228
|
+
c.close();
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
/**
|
|
232
|
+
* Shared by the tenant and service clients: a PUT of raw bytes to the platform's `put_url`.
|
|
233
|
+
* `headers` is empty for presigned URLs (the URL is the credential) and carries the Bearer
|
|
234
|
+
* key only for the platform's own auth-gated upload route.
|
|
235
|
+
*/
|
|
236
|
+
async function uploadTo(fetchImpl, url, body, sizeBytes, timeoutMs, headers = {}) {
|
|
237
|
+
const init = {
|
|
238
|
+
method: "PUT",
|
|
239
|
+
headers: sizeBytes === undefined ? headers : { ...headers, "Content-Length": String(sizeBytes) },
|
|
240
|
+
body: body,
|
|
241
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
242
|
+
};
|
|
243
|
+
// Streaming request bodies need half-duplex in undici (Node) and are rejected otherwise.
|
|
244
|
+
if (typeof ReadableStream !== "undefined" && body instanceof ReadableStream)
|
|
245
|
+
init.duplex = "half";
|
|
246
|
+
let res;
|
|
247
|
+
try {
|
|
248
|
+
res = await fetchImpl(url, init);
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return http_js_1.UNAVAILABLE;
|
|
252
|
+
}
|
|
253
|
+
if (!res.ok)
|
|
254
|
+
return (0, http_js_1.failureOf)(res);
|
|
255
|
+
return { ok: true, status: res.status, data: undefined };
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { PlatformFailure, PlatformResult } from "./types.js";
|
|
2
|
+
export type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
|
|
3
|
+
export declare const UNAVAILABLE: PlatformFailure;
|
|
4
|
+
export declare const trimSlash: (url: string) => string;
|
|
5
|
+
export declare function defaultFetch(): FetchLike;
|
|
6
|
+
export interface Deadline {
|
|
7
|
+
signal: AbortSignal;
|
|
8
|
+
/** Headers arrived within budget: stop the timer but keep the caller's abort wired to the body. */
|
|
9
|
+
clearTimer: () => void;
|
|
10
|
+
/** The request is over (settled, or its body ended/cancelled): timer off, caller listener detached. */
|
|
11
|
+
done: () => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* One AbortSignal from an optional caller signal plus a timeout budget. `AbortSignal.any`
|
|
15
|
+
* is not on Node 20.0, so this composes by hand. `done()` must run when the request is over,
|
|
16
|
+
* or the timer and the listener on the caller's (often long-lived) signal outlive it; for a
|
|
17
|
+
* streamed body call `clearTimer()` at headers and `done()` when the body finishes.
|
|
18
|
+
*/
|
|
19
|
+
export declare function deadline(timeoutMs: number, signal?: AbortSignal): Deadline;
|
|
20
|
+
/** Passes a body through untouched and runs `onEnd` once, when it closes, errors or is cancelled. */
|
|
21
|
+
export declare function untilEnd(body: ReadableStream<Uint8Array>, onEnd: () => void): ReadableStream<Uint8Array>;
|
|
22
|
+
/** Reads the platform error envelope `{error, message, request_id}`; non-JSON bodies keep the http_<status> code. */
|
|
23
|
+
export declare function failureOf(res: Response): Promise<PlatformFailure>;
|
|
24
|
+
/** Resolves a fetch Response into the result envelope; 204 carries `undefined` data. */
|
|
25
|
+
export declare function resultOf<T>(res: Response): Promise<PlatformResult<T>>;
|
|
26
|
+
export declare function queryString(params: object): string;
|
|
27
|
+
export declare const enc: (v: string) => string;
|
package/dist/cjs/http.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enc = exports.trimSlash = exports.UNAVAILABLE = void 0;
|
|
4
|
+
exports.defaultFetch = defaultFetch;
|
|
5
|
+
exports.deadline = deadline;
|
|
6
|
+
exports.untilEnd = untilEnd;
|
|
7
|
+
exports.failureOf = failureOf;
|
|
8
|
+
exports.resultOf = resultOf;
|
|
9
|
+
exports.queryString = queryString;
|
|
10
|
+
exports.UNAVAILABLE = { ok: false, reason: "unavailable" };
|
|
11
|
+
const trimSlash = (url) => url.replace(/\/+$/, "");
|
|
12
|
+
exports.trimSlash = trimSlash;
|
|
13
|
+
function defaultFetch() {
|
|
14
|
+
const f = globalThis.fetch;
|
|
15
|
+
if (typeof f !== "function")
|
|
16
|
+
throw new Error("@awesomate/platform-sdk: no global fetch; pass fetchImpl");
|
|
17
|
+
return (input, init) => f(input, init);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* One AbortSignal from an optional caller signal plus a timeout budget. `AbortSignal.any`
|
|
21
|
+
* is not on Node 20.0, so this composes by hand. `done()` must run when the request is over,
|
|
22
|
+
* or the timer and the listener on the caller's (often long-lived) signal outlive it; for a
|
|
23
|
+
* streamed body call `clearTimer()` at headers and `done()` when the body finishes.
|
|
24
|
+
*/
|
|
25
|
+
function deadline(timeoutMs, signal) {
|
|
26
|
+
const ctrl = new AbortController();
|
|
27
|
+
const timer = setTimeout(() => ctrl.abort(new DOMException("timeout", "TimeoutError")), timeoutMs);
|
|
28
|
+
const onAbort = () => ctrl.abort(signal?.reason);
|
|
29
|
+
if (signal?.aborted)
|
|
30
|
+
onAbort();
|
|
31
|
+
else
|
|
32
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
33
|
+
const clearTimer = () => clearTimeout(timer);
|
|
34
|
+
return {
|
|
35
|
+
signal: ctrl.signal,
|
|
36
|
+
clearTimer,
|
|
37
|
+
done: () => {
|
|
38
|
+
clearTimer();
|
|
39
|
+
signal?.removeEventListener("abort", onAbort);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** Passes a body through untouched and runs `onEnd` once, when it closes, errors or is cancelled. */
|
|
44
|
+
function untilEnd(body, onEnd) {
|
|
45
|
+
const reader = body.getReader();
|
|
46
|
+
let ended = false;
|
|
47
|
+
const end = () => {
|
|
48
|
+
if (ended)
|
|
49
|
+
return;
|
|
50
|
+
ended = true;
|
|
51
|
+
onEnd();
|
|
52
|
+
};
|
|
53
|
+
return new ReadableStream({
|
|
54
|
+
async pull(ctrl) {
|
|
55
|
+
try {
|
|
56
|
+
const { value, done } = await reader.read();
|
|
57
|
+
if (done) {
|
|
58
|
+
end();
|
|
59
|
+
ctrl.close();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
ctrl.enqueue(value);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
end();
|
|
66
|
+
ctrl.error(err);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
cancel(reason) {
|
|
70
|
+
end();
|
|
71
|
+
return reader.cancel(reason);
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const header = (res, name) => {
|
|
76
|
+
const v = res.headers?.get?.(name);
|
|
77
|
+
return v === null || v === undefined ? undefined : v;
|
|
78
|
+
};
|
|
79
|
+
/** Reads the platform error envelope `{error, message, request_id}`; non-JSON bodies keep the http_<status> code. */
|
|
80
|
+
async function failureOf(res) {
|
|
81
|
+
if (res.status >= 500)
|
|
82
|
+
return exports.UNAVAILABLE;
|
|
83
|
+
let envelope = {};
|
|
84
|
+
try {
|
|
85
|
+
envelope = (await res.json());
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// keep the fallback code
|
|
89
|
+
}
|
|
90
|
+
const out = { ok: false, status: res.status, error: envelope.error ?? `http_${res.status}` };
|
|
91
|
+
if (envelope.message !== undefined)
|
|
92
|
+
out.message = envelope.message;
|
|
93
|
+
if (envelope.request_id !== undefined)
|
|
94
|
+
out.requestId = envelope.request_id;
|
|
95
|
+
const retryAfter = header(res, "retry-after");
|
|
96
|
+
if (retryAfter !== undefined)
|
|
97
|
+
out.retryAfter = retryAfter;
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
/** Resolves a fetch Response into the result envelope; 204 carries `undefined` data. */
|
|
101
|
+
async function resultOf(res) {
|
|
102
|
+
if (!res.ok)
|
|
103
|
+
return failureOf(res);
|
|
104
|
+
if (res.status === 204)
|
|
105
|
+
return { ok: true, status: 204, data: undefined };
|
|
106
|
+
try {
|
|
107
|
+
return { ok: true, status: res.status, data: (await res.json()) };
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return exports.UNAVAILABLE;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function queryString(params) {
|
|
114
|
+
const q = new URLSearchParams();
|
|
115
|
+
for (const [k, v] of Object.entries(params)) {
|
|
116
|
+
if (v === undefined || v === null || v === "")
|
|
117
|
+
continue;
|
|
118
|
+
if (typeof v !== "string" && typeof v !== "number" && typeof v !== "boolean")
|
|
119
|
+
continue;
|
|
120
|
+
q.set(k, String(v));
|
|
121
|
+
}
|
|
122
|
+
const s = q.toString();
|
|
123
|
+
return s ? `?${s}` : "";
|
|
124
|
+
}
|
|
125
|
+
const enc = (v) => encodeURIComponent(v);
|
|
126
|
+
exports.enc = enc;
|
|
127
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/http.ts"],"names":[],"mappings":"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"}
|