@lionhummer/constellation 0.2.0 → 0.3.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 CHANGED
@@ -6,30 +6,97 @@ 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
- ```ts
10
- import { Constellation, uuid7 } from "@lionhummer/constellation"
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:
11
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";
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: "https://your-constellation-host.example",
14
- apiKey: process.env.CONSTELLATION_EXECUTION_KEY!,
15
- })
16
-
17
- const { id } = await client.requests.create({
18
- idempotencyKey: uuid7(),
19
- workspaceId: "workspace-123",
20
- externalId: "conversation-456",
21
- model: "catalog/example-model",
22
- instructions: "Answer accurately.",
23
- messages: [{ role: "user", text: "Look this up." }],
24
- tools: [{
25
- name: "lookup",
26
- inputSchema: { type: "object", properties: { key: { type: "string" } } },
27
- }],
28
- events: { afterStep: "wait", outputDelta: "notify", terminal: "notify" },
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
- The first model step uses the submitted input. Keep the idempotency key for retries; reusing it with different input is rejected. `externalId` connects independent requests and may supply a routing affinity hint; it does not load conversation history.
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.3.0
98
+
99
+ 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
100
 
34
101
  ## Events and caller work
35
102
 
@@ -37,8 +104,12 @@ All event modes default to `off`. `beforeModel`, `afterModel`, and `afterStep` a
37
104
 
38
105
  `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
106
 
107
+ 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:
108
+
40
109
  ```ts
41
- const handlers = {
110
+ import type { InputHandlers } from "@lionhummer/constellation"
111
+
112
+ const handlers: InputHandlers = {
42
113
  consumerId: "stable-worker-id",
43
114
  tools: {
44
115
  lookup: async (args, { inputId, signal }) => lookupOnce(args, inputId, signal),
@@ -60,7 +131,7 @@ await client.inputs.handle(id, inputId, handlers)
60
131
 
61
132
  ## Webhook
62
133
 
63
- Configure an HTTPS callback URL and token on the execution key. Verify `x-constellation-webhook-token`; return 2xx after your effects are durable. A non-2xx response is retried. Model text/reasoning chunks arrive as `request.events` when `outputDelta` is `notify`; terminal notification carries a request ID and event sequence.
134
+ 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
135
 
65
136
  ```ts
66
137
  const event = await client.webhooks.verify(request, webhookToken)
@@ -78,4 +149,10 @@ if ("inputId" in event) {
78
149
 
79
150
  `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
151
 
81
- Execution methods are top-level (`models`, `requests`, `inputs`, `spans`, `logs`). Configuration lives under `management`; read-only operational data lives under `telemetry`. 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`.
152
+ 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`.
153
+
154
+ Use the documented namespaces throughout: `requests.create`, `requests.get`,
155
+ `requests.getSummary`, `requests.preview`, `requests.listRecords`,
156
+ `requests.streamEvents`, `inputs.claim`, `inputs.respond`, `spans.start`,
157
+ `spans.add`, and `logs.add`. Their implementation helpers are private; flat
158
+ method aliases are not part of the client API.