@lionhummer/constellation 0.2.0 → 0.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 +145 -23
- package/dist/index.js +573 -545
- package/dist/types/client-core/src/error-details.d.ts +6 -0
- package/dist/{index.d.ts → types/client-core/src/index.d.ts} +107 -213
- package/dist/types/sdk/src/index.d.ts +210 -0
- package/examples/quickstart.mjs +48 -0
- package/package.json +16 -6
package/README.md
CHANGED
|
@@ -6,30 +6,101 @@ TypeScript client for Constellation's durable request API. Run it on a trusted s
|
|
|
6
6
|
npm install @lionhummer/constellation
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
## First request (Node.js 22+)
|
|
10
|
+
|
|
11
|
+
An operator must first configure an enabled provider, its credential reference, a model/offering, and access rules for your workspace. Ask them for the API base URL and an **execution** key. A console login is not an SDK credential. The base URL is the host root, without `/v1` or `/internal`.
|
|
12
|
+
|
|
13
|
+
Set these variables privately in your server environment:
|
|
14
|
+
|
|
15
|
+
- `CONSTELLATION_BASE_URL`: your server's URL (local API: `http://127.0.0.1:8080`).
|
|
16
|
+
- `CONSTELLATION_EXECUTION_KEY`: the secret copied when the operator creates your key.
|
|
17
|
+
- `CONSTELLATION_WORKSPACE_ID`: the exact workspace allowed by the key and access rules.
|
|
18
|
+
- `CONSTELLATION_USER_ID`: required when the key is user-scoped; otherwise optional.
|
|
19
|
+
- `CONSTELLATION_PLAN_ID`: optional pricing plan ID, when your operator supplies one.
|
|
20
|
+
|
|
21
|
+
Run the included example to discover accessible models. This does not dispatch a provider:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
node node_modules/@lionhummer/constellation/examples/quickstart.mjs
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Set `CONSTELLATION_MODEL` to a returned `models[].id` and rerun the command. The second run submits a real provider request, prints its ID/events, then shows `completed` and the output text. Provider usage can incur charges. It needs no webhook, tool handler or wait checkpoint.
|
|
28
|
+
|
|
29
|
+
The complete example is included in the package:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import { Constellation, uuid7 } from "@lionhummer/constellation";
|
|
11
33
|
|
|
34
|
+
function required(name) {
|
|
35
|
+
const value = process.env[name];
|
|
36
|
+
if (!value) throw new Error(`Set ${name} in your server environment.`);
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const workspaceId = required("CONSTELLATION_WORKSPACE_ID");
|
|
41
|
+
const userId = process.env.CONSTELLATION_USER_ID || undefined;
|
|
42
|
+
const planId = process.env.CONSTELLATION_PLAN_ID || undefined;
|
|
12
43
|
const client = new Constellation({
|
|
13
|
-
baseUrl: "
|
|
14
|
-
apiKey:
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
44
|
+
baseUrl: required("CONSTELLATION_BASE_URL"),
|
|
45
|
+
apiKey: required("CONSTELLATION_EXECUTION_KEY"),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Discovery uses the same scope as execution. It does not call a provider.
|
|
49
|
+
const catalog = await client.models.list({ workspaceId, userId, planId });
|
|
50
|
+
console.log("Available models:", JSON.stringify(catalog, null, 2));
|
|
51
|
+
const model = process.env.CONSTELLATION_MODEL;
|
|
52
|
+
if (!model) {
|
|
53
|
+
console.log("Set CONSTELLATION_MODEL to one of the returned models[].id values, then rerun.");
|
|
54
|
+
} else {
|
|
55
|
+
const input = {
|
|
56
|
+
idempotencyKey: uuid7(), // Save this input/key if submission needs retrying.
|
|
57
|
+
workspaceId,
|
|
58
|
+
userId,
|
|
59
|
+
planId,
|
|
60
|
+
model,
|
|
61
|
+
messages: [{ role: "user", text: "Say hello in one short sentence." }],
|
|
62
|
+
maxOutputTokens: 256,
|
|
63
|
+
timeoutMs: 120_000,
|
|
64
|
+
// No tools or wait checkpoints: no webhook or input consumer is needed.
|
|
65
|
+
};
|
|
66
|
+
const { id } = await client.requests.create(input);
|
|
67
|
+
console.log("Request ID:", id);
|
|
68
|
+
for await (const event of client.requests.streamEvents(id, {
|
|
69
|
+
signal: AbortSignal.timeout(150_000),
|
|
70
|
+
})) {
|
|
71
|
+
console.log(event.seq, event.type);
|
|
72
|
+
}
|
|
73
|
+
const { request } = await client.requests.getSummary(id);
|
|
74
|
+
console.log("Final status:", request.status);
|
|
75
|
+
console.log("Output:", request.output?.text ?? "");
|
|
76
|
+
if (request.status !== "completed") {
|
|
77
|
+
throw new Error(`Request ${id} ended as ${request.status}: ${request.reason ?? "inspect the request error and attempts"}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
30
80
|
```
|
|
31
81
|
|
|
32
|
-
|
|
82
|
+
Each invocation creates a new request. For application retries, persist the complete input and its UUIDv7 idempotency key **before** submission and reuse both after an uncertain POST; do not generate a fresh key on each retry. `externalId` groups independent requests and may supply a routing-affinity hint; it does not load conversation history. The observer's abort only stops observing; use `requests.cancel(id)` to cancel execution.
|
|
83
|
+
|
|
84
|
+
## Troubleshooting
|
|
85
|
+
|
|
86
|
+
| Symptom | What to check |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| 401 | Correct host and execution key; the key may have been revoked. Browser cookies do not authenticate the SDK. |
|
|
89
|
+
| 403 | Execution permission, matching workspace/user scope and model access rules. Management/telemetry permissions do not imply execution. |
|
|
90
|
+
| No models | Operator has enabled a provider, model and offering with an eligible route/access rule. Discovery uses your workspace/user scope. |
|
|
91
|
+
| 409 | Read the error code/message. Reuse the exact original input for an idempotency retry; renew an expired input claim before answering. |
|
|
92
|
+
| 429 / 503 | Respect `Retry-After`. Retry uncertain admission with the saved input/key. |
|
|
93
|
+
| `queued` | A worker must be running and connected to the same PostgreSQL/Redis environment. |
|
|
94
|
+
| `waiting` | Handle the pending input. Tools always require a consumer; wait checkpoints do too. SSE observation does not answer inputs. |
|
|
95
|
+
| `failed` | Inspect `request.reason`, `request.error`, attempts and timeline for provider, routing or deadline failure. |
|
|
96
|
+
|
|
97
|
+
## Release 0.4.0
|
|
98
|
+
|
|
99
|
+
Telemetry now returns metadata by default. Use `include: "content"` for full content, with the same permission/scope. Adds key-aware `discover`, explicit request timings, accurate endpoint filter types, HTTP chunk pagination, and billing/traffic methods. Deploy the matching server before using these methods.
|
|
100
|
+
|
|
101
|
+
## Release 0.3.0
|
|
102
|
+
|
|
103
|
+
This pre-1.0 release breaks the earlier SDK contract. Use namespaced execution methods; flat aliases and the old drive/protocol surface are removed. Keys, credentials, users and capacity administration are internal-only. Provider-definition writes also require an internal operator. External telemetry and other registry authoring remain available. Deploy a server containing these API changes before switching clients; publishing this package does not deploy the server or upgrade any downstream application dependency.
|
|
33
104
|
|
|
34
105
|
## Events and caller work
|
|
35
106
|
|
|
@@ -37,8 +108,12 @@ All event modes default to `off`. `beforeModel`, `afterModel`, and `afterStep` a
|
|
|
37
108
|
|
|
38
109
|
`afterStep` happens after the model when it has no tool calls, or after all tool results. It includes output, usage, cost, and tool results. Use it to settle billing once and decide whether to complete or continue. Continuing requires a complete next `stepInput`, including model, messages, instructions, tools, parameters, output limit, and cache policy. Constellation routes each later step separately.
|
|
39
110
|
|
|
111
|
+
The following integration sketches contain application-owned functions such as `lookupOnce`, `settleOnce` and `buildCompleteNextStep`; implement those before enabling tools or wait checkpoints. Type handlers explicitly so TypeScript checks their callback arguments:
|
|
112
|
+
|
|
40
113
|
```ts
|
|
41
|
-
|
|
114
|
+
import type { InputHandlers } from "@lionhummer/constellation"
|
|
115
|
+
|
|
116
|
+
const handlers: InputHandlers = {
|
|
42
117
|
consumerId: "stable-worker-id",
|
|
43
118
|
tools: {
|
|
44
119
|
lookup: async (args, { inputId, signal }) => lookupOnce(args, inputId, signal),
|
|
@@ -60,7 +135,7 @@ await client.inputs.handle(id, inputId, handlers)
|
|
|
60
135
|
|
|
61
136
|
## Webhook
|
|
62
137
|
|
|
63
|
-
Configure an HTTPS callback URL and token on the execution key. Verify `x-constellation-webhook-token`; return 2xx after your effects are durable.
|
|
138
|
+
Configure an HTTPS callback URL and token on the execution key. Verify `x-constellation-webhook-token`; return 2xx after your effects are durable. Transient non-2xx responses are retried. A permanent input rejection can use HTTP 422 with the documented `constellation_input_rejected` body; repeated 401/403 stops delivery. Return 2xx only after durable effects. Model text/reasoning chunks arrive as `request.events` when `outputDelta` is `notify`; terminal notification carries a request ID and event sequence.
|
|
64
139
|
|
|
65
140
|
```ts
|
|
66
141
|
const event = await client.webhooks.verify(request, webhookToken)
|
|
@@ -78,4 +153,51 @@ if ("inputId" in event) {
|
|
|
78
153
|
|
|
79
154
|
`requests.get` returns the request and its records. For long executions, use `requests.getSummary`, `requests.listRecords`, `requests.getTimeline`, and the paginated request list. `requests.listEvents` reads the durable event journal; `requests.streamEvents` reconnects SSE from a cursor. Stopping an observer does not cancel execution; use `requests.cancel` explicitly.
|
|
80
155
|
|
|
81
|
-
Execution methods are top-level (`models`, `requests`, `inputs`, `spans`, `logs`).
|
|
156
|
+
Execution methods are top-level (`models`, `requests`, `inputs`, `spans`, `logs`). Registry authoring lives under `management.registry`; read-only operational data lives under `telemetry`. API-key administration, provider credentials, users and capacity controls are internal operator APIs and are absent from this external SDK. The SDK requires a bearer key, omits browser cookies, and has no `/internal` routing option. Provider definitions are readable externally; imports, changes and rollback that modify providers require internal operator access because endpoints and credential references control where stored secrets are sent. The same API timeline is shown in the console. Caller spans can attach to a wait using `inputId` and nest beneath a span using `parentSpanId`.
|
|
157
|
+
|
|
158
|
+
Use the documented namespaces throughout: `requests.create`, `requests.get`,
|
|
159
|
+
`requests.getSummary`, `requests.preview`, `requests.listRecords`,
|
|
160
|
+
`requests.streamEvents`, `inputs.claim`, `inputs.respond`, `spans.start`,
|
|
161
|
+
`spans.add`, and `logs.add`. Their implementation helpers are private; flat
|
|
162
|
+
method aliases are not part of the client API.
|
|
163
|
+
|
|
164
|
+
## Caller failures and delivery retries
|
|
165
|
+
|
|
166
|
+
A failed webhook delivery is one attempt, not the final request outcome. Constellation retains failed attempts even when a later retry succeeds. Retryable delivery warnings include the input ID, attempt number, callback HTTP failure and retry delay; terminal failures remain errors. The callback drawer shows that delivery and the current request state.
|
|
167
|
+
|
|
168
|
+
The SDK records checkpoint and tool exceptions in `attributes.error` on the caller span and preserves the original exception. Root-cause name, bounded message and optional code are included; SQL dumps, stack traces and arbitrary error object fields are omitted, and common credential forms are redacted. This is best-effort redaction: keep sensitive values out of exception messages. Closed error spans with these details produce one linked request error log. Keep complete diagnostics in your application's own logs. Historical spans without error attributes cannot reconstruct the original exception.
|
|
169
|
+
|
|
170
|
+
## Discover available HTTP operations
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
const contract = await client.discover();
|
|
174
|
+
console.log(contract["x-constellation-access"]);
|
|
175
|
+
console.log(Object.keys(contract.paths));
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`discover(signal?)` calls `GET /v1/discover` with the client's bearer key and returns a typed OpenAPI document filtered to that key's permissions and workspace/user scope. It works with execution, management or telemetry keys. `/v1/openapi.json` is the secondary HTTP path. This describes available operations; resource ownership, model access and request lifecycle are still validated when calling them. Discovery does not grant additional access. Requires a server with the discovery endpoint deployed.
|
|
179
|
+
|
|
180
|
+
## Scoped telemetry and timing
|
|
181
|
+
|
|
182
|
+
Telemetry returns metadata by default: request identity/status, timestamps, costs, token counts, attempt commitment/fallback flags and record metadata. Full request/log/HTTP content requires `include: "content"`; this uses the same telemetry permission and key scope, with no extra permission. SDK content fields are optional because the default omits them. Workspace and user scope applies to hot and archived requests, events, records, HTTP, timing and timeline navigation. Global telemetry endpoints require an unscoped key.
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
const page = await client.telemetry.requests.list({ externalId: "thread-123", limit: 25 });
|
|
186
|
+
for (const request of page.items) {
|
|
187
|
+
const timing = await client.telemetry.requests.getTimings(request.id);
|
|
188
|
+
console.log(request.status, timing.queueMs, timing.attempts);
|
|
189
|
+
}
|
|
190
|
+
const detail = await client.telemetry.requests.get(requestId, { include: "content" });
|
|
191
|
+
const logs = await client.telemetry.requests.listRecords(requestId, "logs", {
|
|
192
|
+
level: "warn", offset: 0, limit: 25, include: "content",
|
|
193
|
+
});
|
|
194
|
+
const body = await client.telemetry.requests.getHttpBody(requestId, attemptId, {
|
|
195
|
+
direction: "response", offset: 0, limit: 16384, include: "content",
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`requests.getTimings` is also available with execution authority. Timing responses include admission, queue, worker preparation, provider attempts and caller waits. Admission and preparation have recorded child/detail spans; IDs and provenance match the console. Concurrent waits count once in `callerWaitMs`. Missing/incomplete measurements are null. Read `complete`, `truncated` and `asOf`; grouped totals can overlap and are not additive.
|
|
200
|
+
|
|
201
|
+
Request lists accept `RequestListQuery`, including exact `externalId` and requested-model filters, exclusions and substring matches. Record lists accept `RequestRecordsQuery` (`id`, `q`, log-only `level`, `offset`, `limit`). `PageQuery.filters` belongs only to registry list rows. Every other method has its endpoint's exact query type. See the [HTTP filter matrix](../../docs/api.md#filter-audit-and-exact-query-support) for search semantics, caps, cursor behavior and capabilities not currently provided.
|
|
202
|
+
|
|
203
|
+
Unscoped telemetry also exposes `observability.getBilling`, `getTraffic`, `getTiming`, dashboard and measurement series. Use `providerId` for local series and `upstream` for gateway series. `system.getComponent(component, { status, statusNot, q, offset })` and `jobs.listHistory` page matching operational records; notifications use fixed 25-item pages and status/offset, not generic filters.
|