@kindgi/sdk 0.1.1 → 0.1.3
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/package.json +23 -17
- package/skills/kindgi-authoring-guardrails/SKILL.md +8 -2
- package/skills/kindgi-authoring-providers/SKILL.md +40 -5
- package/skills/kindgi-authoring-tools/SKILL.md +14 -2
- package/skills/kindgi-getting-started/SKILL.md +77 -3
- package/skills/kindgi-python-authoring-tools/SKILL.md +4 -0
- package/skills/kindgi-python-getting-started/SKILL.md +73 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kindgi/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "@kindgi/sdk — the authoring SDK for Kindgi™. Facade over the individual @kindgi/* packages + @kindgi/client. Unifies pack authoring (defineTool / defineCheck / defineAgent / defineFlow) and client callsites (createClient) behind three sub-paths: /define, /client, /types. Re-export facade; zero behavior.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -18,27 +18,33 @@
|
|
|
18
18
|
"exports": {
|
|
19
19
|
"./define": {
|
|
20
20
|
"types": "./dist/define.d.ts",
|
|
21
|
-
"import": "./dist/define.js"
|
|
21
|
+
"import": "./dist/define.js",
|
|
22
|
+
"default": "./dist/define.js"
|
|
22
23
|
},
|
|
23
24
|
"./client": {
|
|
24
25
|
"types": "./dist/client.d.ts",
|
|
25
|
-
"import": "./dist/client.js"
|
|
26
|
+
"import": "./dist/client.js",
|
|
27
|
+
"default": "./dist/client.js"
|
|
26
28
|
},
|
|
27
29
|
"./types": {
|
|
28
30
|
"types": "./dist/types.d.ts",
|
|
29
|
-
"import": "./dist/types.js"
|
|
31
|
+
"import": "./dist/types.js",
|
|
32
|
+
"default": "./dist/types.js"
|
|
30
33
|
},
|
|
31
34
|
"./webhooks": {
|
|
32
35
|
"types": "./dist/webhooks.d.ts",
|
|
33
|
-
"import": "./dist/webhooks.js"
|
|
36
|
+
"import": "./dist/webhooks.js",
|
|
37
|
+
"default": "./dist/webhooks.js"
|
|
34
38
|
},
|
|
35
39
|
"./build": {
|
|
36
40
|
"types": "./dist/build.d.ts",
|
|
37
|
-
"import": "./dist/build.js"
|
|
41
|
+
"import": "./dist/build.js",
|
|
42
|
+
"default": "./dist/build.js"
|
|
38
43
|
},
|
|
39
44
|
".": {
|
|
40
45
|
"types": "./dist/index.d.ts",
|
|
41
|
-
"import": "./dist/index.js"
|
|
46
|
+
"import": "./dist/index.js",
|
|
47
|
+
"default": "./dist/index.js"
|
|
42
48
|
},
|
|
43
49
|
"./package.json": "./package.json"
|
|
44
50
|
},
|
|
@@ -49,15 +55,15 @@
|
|
|
49
55
|
"README.md"
|
|
50
56
|
],
|
|
51
57
|
"dependencies": {
|
|
52
|
-
"@kindgi/agents": "0.1.
|
|
53
|
-
"@kindgi/client": "0.1.
|
|
54
|
-
"@kindgi/crypto": "0.1.
|
|
55
|
-
"@kindgi/flow": "0.1.
|
|
56
|
-
"@kindgi/guardrails": "0.1.
|
|
57
|
-
"@kindgi/handler-runtime": "0.1.
|
|
58
|
-
"@kindgi/schema": "0.1.
|
|
59
|
-
"@kindgi/tools": "0.1.
|
|
60
|
-
"@kindgi/types": "0.1.
|
|
58
|
+
"@kindgi/agents": "0.1.3",
|
|
59
|
+
"@kindgi/client": "0.1.3",
|
|
60
|
+
"@kindgi/crypto": "0.1.3",
|
|
61
|
+
"@kindgi/flow": "0.1.3",
|
|
62
|
+
"@kindgi/guardrails": "0.1.3",
|
|
63
|
+
"@kindgi/handler-runtime": "0.1.3",
|
|
64
|
+
"@kindgi/schema": "0.1.3",
|
|
65
|
+
"@kindgi/tools": "0.1.3",
|
|
66
|
+
"@kindgi/types": "0.1.3"
|
|
61
67
|
},
|
|
62
68
|
"peerDependencies": {
|
|
63
69
|
"zod": "^4.0.0"
|
|
@@ -75,7 +81,7 @@
|
|
|
75
81
|
"vitest": "^2.1.8"
|
|
76
82
|
},
|
|
77
83
|
"engines": {
|
|
78
|
-
"node": ">=22.
|
|
84
|
+
"node": ">=22.12.0"
|
|
79
85
|
},
|
|
80
86
|
"publishConfig": {
|
|
81
87
|
"access": "public",
|
|
@@ -107,11 +107,17 @@ How the pack tooling reads this file:
|
|
|
107
107
|
`configZod`, `configSchema` or `configJsonSchema`, or a top-level
|
|
108
108
|
`configZod` / `configSchema`), and the declaration's `config` — what
|
|
109
109
|
the check runs with. It does not record `description`, `budget` or
|
|
110
|
-
`judgeCapabilities`.
|
|
110
|
+
`judgeCapabilities`. It checks `config` (none counts as `{}`) against
|
|
111
|
+
the config schema: a config that doesn't fit, or a required field with
|
|
112
|
+
no default left out, is a file error naming where, and `kindgi build`
|
|
113
|
+
refuses the pack.
|
|
111
114
|
- The **pack service** loads the same module to run the check. It uses
|
|
112
115
|
the module's `evaluate` export, or the `default` / `check` export when
|
|
113
116
|
that is a function or has an `evaluate` method — here, the named
|
|
114
|
-
`check` export.
|
|
117
|
+
`check` export. A `defineCheck` check's `evaluate` gets the config its
|
|
118
|
+
schema resolves: the schema's defaults applied (a guardrail that
|
|
119
|
+
declares no config gets them all), and a config that doesn't fit
|
|
120
|
+
refused, naming where.
|
|
115
121
|
|
|
116
122
|
## Validating a declaration in-process
|
|
117
123
|
|
|
@@ -22,7 +22,7 @@ description: >
|
|
|
22
22
|
kindgi-getting-started.
|
|
23
23
|
type: core
|
|
24
24
|
library: "@kindgi/sdk"
|
|
25
|
-
version: "0.9.
|
|
25
|
+
version: "0.9.3"
|
|
26
26
|
sdk_version: "0.0.0"
|
|
27
27
|
pack_languages: [node, python]
|
|
28
28
|
sources:
|
|
@@ -126,12 +126,46 @@ credential on argv.
|
|
|
126
126
|
kindgi providers register --preset=anthropic # Opus 5.5, Sonnet 5.5, Haiku 4.5
|
|
127
127
|
kindgi providers register --preset=anthropic --models=claude-haiku-4-5 # just one
|
|
128
128
|
```
|
|
129
|
-
The preset carries the models, context windows and current
|
|
130
|
-
(`kindgi providers presets` lists the presets and when their prices
|
|
131
|
-
checked). In a pack it refuses until the key is in the pack's env files —
|
|
129
|
+
The preset carries the models, context windows, output limits and current
|
|
130
|
+
prices (`kindgi providers presets` lists the presets and when their prices
|
|
131
|
+
were checked); `--max-output-tokens=<n>` sets another output limit. In a pack it refuses until the key is in the pack's env files —
|
|
132
132
|
step 1. That's all for Anthropic; go to step 4. The rest of this path is
|
|
133
133
|
the same registration by hand, for a spec of your own.
|
|
134
134
|
|
|
135
|
+
**Or declare it in the pack's config**, and `kindgi dev` registers it on
|
|
136
|
+
every boot: in every git worktree (each has its own dev database), after
|
|
137
|
+
`--reset`, and on a teammate's machine. Prefer this for any provider the pack
|
|
138
|
+
needs under `kindgi dev`:
|
|
139
|
+
```ts
|
|
140
|
+
// in kindgi.config.ts
|
|
141
|
+
providers: [
|
|
142
|
+
{ preset: 'anthropic', models: ['claude-haiku-4-5'] }, // key ANTHROPIC_API_KEY, from the env files
|
|
143
|
+
{ preset: 'gemini', project: 'acme-gcp', models: ['gemini-2.5-flash'] },
|
|
144
|
+
{ spec: { /* the provider.json body below */ } },
|
|
145
|
+
],
|
|
146
|
+
```
|
|
147
|
+
```toml
|
|
148
|
+
# in pyproject.toml: one table per provider, same keys
|
|
149
|
+
[[tool.kindgi.providers]]
|
|
150
|
+
preset = "anthropic"
|
|
151
|
+
models = ["claude-haiku-4-5"]
|
|
152
|
+
```
|
|
153
|
+
- A preset entry takes `models`, `project`, `secret` (the key's name, in place
|
|
154
|
+
of the preset's) and `maxOutputTokens`, spelled the same in `pyproject.toml`;
|
|
155
|
+
a `spec` entry is a `--spec` body. A
|
|
156
|
+
key is always a secret's name (`secret_ref`); a credential in
|
|
157
|
+
`adapter_config` is refused.
|
|
158
|
+
- Each boot prints `Providers from kindgi.config.ts:` with one line each:
|
|
159
|
+
`registered`, `unchanged`, `registered again (changed in kindgi.config.ts)`,
|
|
160
|
+
`unregistered (no longer in kindgi.config.ts)`, or ⚠ `not registered: <KEY>
|
|
161
|
+
is not in .env, .env.local` (set the key, then restart: the config isn't
|
|
162
|
+
watched).
|
|
163
|
+
- A provider with that id that `kindgi dev` didn't register is left as it is;
|
|
164
|
+
if its region or models differ, a ⚠ line names the
|
|
165
|
+
`kindgi providers unregister` that lets the config's version apply.
|
|
166
|
+
- A runtime `kindgi dev` doesn't run (staging, production) still gets its
|
|
167
|
+
providers with `kindgi providers register`.
|
|
168
|
+
|
|
135
169
|
**Step 2 (by hand) — write `provider.json`** at the pack root. One connection,
|
|
136
170
|
three models — matches how the Anthropic SDK actually works (the API
|
|
137
171
|
key is per-vendor; the model is per-call):
|
|
@@ -438,7 +472,7 @@ outside Google Cloud: put a service-account key (its JSON) in a secret
|
|
|
438
472
|
"name": "gemini-2.5-pro",
|
|
439
473
|
"contextWindow": 1048576,
|
|
440
474
|
"features": ["tool-use"],
|
|
441
|
-
"maxOutputTokens":
|
|
475
|
+
"maxOutputTokens": 65536,
|
|
442
476
|
"cost": {
|
|
443
477
|
"promptUsdPer1kTokens": 0.00125,
|
|
444
478
|
"completionUsdPer1kTokens": 0.01,
|
|
@@ -453,6 +487,7 @@ outside Google Cloud: put a service-account key (its JSON) in a secret
|
|
|
453
487
|
"name": "gemini-2.5-flash",
|
|
454
488
|
"contextWindow": 1048576,
|
|
455
489
|
"features": ["tool-use"],
|
|
490
|
+
"maxOutputTokens": 65536,
|
|
456
491
|
"cost": { "promptUsdPer1kTokens": 0.0003, "completionUsdPer1kTokens": 0.0025 }
|
|
457
492
|
}
|
|
458
493
|
]
|
|
@@ -12,7 +12,7 @@ description: >
|
|
|
12
12
|
authoring agents is covered by kindgi-authoring-agents.
|
|
13
13
|
type: core
|
|
14
14
|
library: "@kindgi/sdk"
|
|
15
|
-
version: "0.4.
|
|
15
|
+
version: "0.4.4"
|
|
16
16
|
sdk_version: "0.0.0"
|
|
17
17
|
pack_languages: [node]
|
|
18
18
|
sources:
|
|
@@ -88,7 +88,8 @@ const defined = defineTool({
|
|
|
88
88
|
effects: [],
|
|
89
89
|
mutating: false,
|
|
90
90
|
handler: async (input, ctx) => {
|
|
91
|
-
// ctx.tenantId, ctx.abortSignal, ctx.secrets (what needsSpec declares) available
|
|
91
|
+
// ctx.tenantId, ctx.abortSignal, ctx.secrets (what needsSpec declares) available;
|
|
92
|
+
// in a run, ctx.projectId and ctx.orgId (set from the run, never from the input)
|
|
92
93
|
// Return type MUST match Output schema (validated at invoke time)
|
|
93
94
|
return { found: true, canonicalCite: '...' };
|
|
94
95
|
},
|
|
@@ -278,6 +279,17 @@ run start via `semver.maxSatisfying`. No implicit `:latest`.
|
|
|
278
279
|
("⚠ The pack imports @prisma/client (in kindgi/tools/…), which
|
|
279
280
|
package.json lists only in devDependencies: …"), and `kindgi build`
|
|
280
281
|
refuses the pack until it moves.
|
|
282
|
+
10. **A tool that needs the app's install scripts in the image.** The
|
|
283
|
+
image installs with scripts off, so the app's `postinstall` /
|
|
284
|
+
`prepare` (`prisma generate`, husky) don't run there; `kindgi build`
|
|
285
|
+
lists them ("✓ The app's own install scripts don't run in the image:
|
|
286
|
+
…"). A tool that uses Prisma's client then fails the build ("@prisma/client
|
|
287
|
+
did not initialize yet"). Add `prisma({ schema: 'prisma/schema.prisma' })`
|
|
288
|
+
(from `@kindgi/sdk/build`; add `config: 'prisma.config.ts'` when the app
|
|
289
|
+
has one) to `image.extensions` in `kindgi.config.ts`. Other generate
|
|
290
|
+
steps: `defineBuildExtension({ name, contextFiles, postInstall: [{ bin, args }] })`.
|
|
291
|
+
Debian packages: `image.systemPackages`. Placeholder env for those steps:
|
|
292
|
+
`image.buildEnv` (never secrets).
|
|
281
293
|
|
|
282
294
|
## References
|
|
283
295
|
|
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
primitive.
|
|
15
15
|
type: core
|
|
16
16
|
library: "@kindgi/sdk"
|
|
17
|
-
version: "0.3.
|
|
17
|
+
version: "0.3.7"
|
|
18
18
|
sdk_version: "0.0.0"
|
|
19
19
|
pack_languages: [node]
|
|
20
20
|
---
|
|
@@ -158,8 +158,82 @@ Most of the setup is automatable, but two require your knowledge:
|
|
|
158
158
|
- **Real LLM provider credentials** — the built-in dev-echo provider
|
|
159
159
|
returns canned responses (great for the loop test, useless for real
|
|
160
160
|
agents). It is a fallback, so it steps aside once a real provider is
|
|
161
|
-
registered
|
|
162
|
-
key in `.env
|
|
161
|
+
registered. Declare it in `kindgi.config.ts`
|
|
162
|
+
(`providers: [{ preset: 'anthropic' }]`, the key in `.env`) and `kindgi dev`
|
|
163
|
+
registers it on every boot, in every worktree and after `--reset`; or once,
|
|
164
|
+
by hand: `kindgi providers register --preset=anthropic`. See
|
|
165
|
+
`kindgi-authoring-providers`.
|
|
166
|
+
|
|
167
|
+
## Your app and Kindgi's data
|
|
168
|
+
|
|
169
|
+
When the app keeps something a run did (a ticket a flow triaged, an answer
|
|
170
|
+
an agent gave), its own row stores the run's id, in a column such as
|
|
171
|
+
`kindgi_run_id`. The app starts the run and reads the rest through the API,
|
|
172
|
+
server side, with `createClient()` from `@kindgi/sdk/client`:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { createClient } from '@kindgi/sdk/client';
|
|
176
|
+
|
|
177
|
+
const kindgi = createClient(); // KINDGI_API_URL + KINDGI_API_TOKEN
|
|
178
|
+
const run = await kindgi.runs.start({
|
|
179
|
+
flow: 'my-pack.triage-ticket',
|
|
180
|
+
input: { ticketId },
|
|
181
|
+
options: { wait: false }, // the run id now; run.finished tells you when it ends
|
|
182
|
+
});
|
|
183
|
+
// save run.id as the ticket's kindgi_run_id
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
- **Status, output, timing:** `kindgi.runs.get(runId)`
|
|
188
|
+
(`GET /v1/runs/{runId}`); status and timing only: `kindgi.runs.progress(runId)`.
|
|
189
|
+
- **The audit, step by step:** `kindgi.runs.journal(runId)`
|
|
190
|
+
(`GET /v1/runs/{runId}/journal`).
|
|
191
|
+
- **Where an agent's answer came from:** `kindgi.provenance.get(runId)`
|
|
192
|
+
(`GET /v1/provenance/{runId}`). A flow run has none of its own: each agent
|
|
193
|
+
step's `step.completed` entry in the flow's journal names its turn's run
|
|
194
|
+
(`payload.output.runId`).
|
|
195
|
+
- **What it cost:** `kindgi.cost.usage.query({ rootRunId: runId })`
|
|
196
|
+
(`GET /v1/cost/records?rootRunId={runId}`): `.items`, one record per model
|
|
197
|
+
call, with its `model`, `usage` (`promptTokens`, `completionTokens`) and
|
|
198
|
+
`costUsd` (a number, US dollars), a flow's agent steps included. For one
|
|
199
|
+
customer's spend, see below.
|
|
200
|
+
- **When a run finished:** the `run.finished` webhook (the run's id, its
|
|
201
|
+
outcome and its cost in `data.run.usage`; no output, so then `runs.get`),
|
|
202
|
+
not polling.
|
|
203
|
+
|
|
204
|
+
**One customer's spend:** give each customer an org, and start their runs in a
|
|
205
|
+
project of that org. Then one call sums their month:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
import type { Timestamp } from '@kindgi/sdk/types';
|
|
209
|
+
|
|
210
|
+
// Once per customer. Project slugs are unique across the tenant: put the customer in them.
|
|
211
|
+
const org = await kindgi.orgs.create({ slug: 'acme-customer-one', name: 'Customer one' });
|
|
212
|
+
const project = await kindgi.projects.create({ orgId: org.id, slug: 'acme-customer-one-app', name: 'App' });
|
|
213
|
+
// save org.id and project.id on the customer's row; start their runs with projectId: project.id
|
|
214
|
+
|
|
215
|
+
const now = new Date();
|
|
216
|
+
const month = await kindgi.cost.usage.summary({
|
|
217
|
+
scope: { kind: 'org', orgId: org.id },
|
|
218
|
+
from: new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)).toISOString() as Timestamp,
|
|
219
|
+
to: now.toISOString() as Timestamp, // exclusive
|
|
220
|
+
groupBy: ['month'],
|
|
221
|
+
});
|
|
222
|
+
// month.totalUsd; month.groups[i].key ({ month: '2026-10' }), .totalUsd, .tokens
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Docs: https://docs.kindgi.com/v0.1/guides/observability/cost-per-run/
|
|
226
|
+
|
|
227
|
+
Show it in the app's own UI. **Never:**
|
|
228
|
+
|
|
229
|
+
- **query Kindgi's database**, even on the app's own Postgres server, and
|
|
230
|
+
never map its tables into the app's ORM. Its schema is private and changes
|
|
231
|
+
with every release (migrations only go forward), row-level security guards
|
|
232
|
+
every tenant query, and a runtime Kindgi hosts gives no database access.
|
|
233
|
+
- **link users to Kindgi's console** or any Kindgi UI for this data.
|
|
234
|
+
|
|
235
|
+
To keep a copy (reporting, search), pull it through the API into the app's
|
|
236
|
+
own tables. Docs: https://docs.kindgi.com/v0.1/guides/runs/show-runs-in-your-app/
|
|
163
237
|
|
|
164
238
|
## References
|
|
165
239
|
|
|
@@ -103,6 +103,10 @@ def verify_citation(citation: Citation, ctx: ToolContext) -> Verdict:
|
|
|
103
103
|
- `ctx.run_id` — the run (an agent turn or a flow step) the call belongs to.
|
|
104
104
|
- `ctx.request_id` — this call, e.g. the model's tool-call id; useful
|
|
105
105
|
for logs and idempotency keys.
|
|
106
|
+
- `ctx.project_id`, `ctx.org_id` — the run's project, and that project's
|
|
107
|
+
org (`None` when it has none). The runtime sets them from the run, never
|
|
108
|
+
from the input: to check an org or project id the input names, compare
|
|
109
|
+
it with these instead of trusting it.
|
|
106
110
|
- `ctx.cancellation` — fires when the call's deadline passes or the
|
|
107
111
|
caller disconnects. An `async def` handler is also cancelled at its
|
|
108
112
|
next `await`. A `def` handler keeps running in its thread: check
|
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
kindgi-python-authoring-agents; models by kindgi-authoring-providers.
|
|
15
15
|
type: core
|
|
16
16
|
library: "kindgi (Python)"
|
|
17
|
-
version: "0.1.
|
|
17
|
+
version: "0.1.5"
|
|
18
18
|
sdk_version: "0.0.0"
|
|
19
19
|
pack_languages: [python]
|
|
20
20
|
sources:
|
|
@@ -26,7 +26,7 @@ sources:
|
|
|
26
26
|
# Getting started with Kindgi in Python
|
|
27
27
|
|
|
28
28
|
> **Running `kindgi`:** a Python pack has no Node project, so the
|
|
29
|
-
> `kindgi` CLI (a Node 22+ program) is the one on `PATH`. Python
|
|
29
|
+
> `kindgi` CLI (a Node 22.12+ program) is the one on `PATH`. Python
|
|
30
30
|
> commands run in the pack's environment: `uv run …`.
|
|
31
31
|
|
|
32
32
|
## What a pack is
|
|
@@ -129,8 +129,16 @@ kindgi secrets set ANTHROPIC_API_KEY --env=local --scope=tenant # no-echo prom
|
|
|
129
129
|
kindgi providers register --preset=anthropic
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
It takes over at the next turn.
|
|
133
|
-
`kindgi
|
|
132
|
+
It takes over at the next turn. That registration is in this project's dev
|
|
133
|
+
database only. To have `kindgi dev` register it on every boot, in every
|
|
134
|
+
worktree and after `--reset`, declare it in `pyproject.toml` instead:
|
|
135
|
+
|
|
136
|
+
```toml
|
|
137
|
+
[[tool.kindgi.providers]]
|
|
138
|
+
preset = "anthropic" # its key, ANTHROPIC_API_KEY, from the env files
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Details and other providers: `kindgi-authoring-providers`.
|
|
134
142
|
|
|
135
143
|
`uv run python -m kindgi.pack index --pack-dir .` prints what Kindgi
|
|
136
144
|
sees (the index); `kindgi dev` reports a broken file with its path and
|
|
@@ -182,6 +190,67 @@ for event in client.runs.stream(str(run.id)):
|
|
|
182
190
|
`AsyncKindgi` is the asyncio twin. `kindgi dev` prints the URL and the
|
|
183
191
|
token; `.kindgirc.json` in the pack holds them for the CLI.
|
|
184
192
|
|
|
193
|
+
## Your app and Kindgi's data
|
|
194
|
+
|
|
195
|
+
When the app keeps something a run did (a ticket a flow triaged, an answer
|
|
196
|
+
an agent gave), its own row stores the run's id, in a column such as
|
|
197
|
+
`kindgi_run_id` (`run = client.runs.start(flow=…, input=…, options={"wait": False})`,
|
|
198
|
+
then `run.id`). The app reads the rest through the API, server side, with
|
|
199
|
+
`Kindgi()` from `kindgi.client`:
|
|
200
|
+
|
|
201
|
+
- **Status, output, timing:** `client.runs.get(run_id)`
|
|
202
|
+
(`GET /v1/runs/{runId}`); status and timing only: `client.runs.progress(run_id)`.
|
|
203
|
+
- **The audit, step by step:** `client.runs.journal(run_id).data`
|
|
204
|
+
(`GET /v1/runs/{runId}/journal`).
|
|
205
|
+
- **Where an agent's answer came from:** `client.provenance.get(run_id)`
|
|
206
|
+
(`GET /v1/provenance/{runId}`). A flow run has none of its own: each agent
|
|
207
|
+
step's `step.completed` entry in the flow's journal names its turn's run
|
|
208
|
+
(`entry.payload["output"]["runId"]`).
|
|
209
|
+
- **What it cost:** `client.cost.records.list(root_run_id=run_id).data`
|
|
210
|
+
(`GET /v1/cost/records?rootRunId={runId}`): one record per model call,
|
|
211
|
+
with its `model`, `usage` (`prompt_tokens`, `completion_tokens`) and
|
|
212
|
+
`cost_usd` (a float, US dollars), a flow's agent steps included. For one
|
|
213
|
+
customer's spend, see below.
|
|
214
|
+
- **When a run finished:** the `run.finished` webhook (the run's id, its
|
|
215
|
+
outcome and its cost in `data.run.usage`; no output, so then `runs.get`),
|
|
216
|
+
not polling.
|
|
217
|
+
|
|
218
|
+
**One customer's spend:** give each customer an org, and start their runs in a
|
|
219
|
+
project of that org. Then one call sums their month:
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
from datetime import datetime, timezone
|
|
223
|
+
|
|
224
|
+
# Once per customer. Project slugs are unique across the tenant: put the customer in them.
|
|
225
|
+
org = client.orgs.create(slug="acme-customer-one", name="Customer one")
|
|
226
|
+
project = client.projects.create(org_id=str(org.id), slug="acme-customer-one-app", name="App")
|
|
227
|
+
# save org.id and project.id on the customer's row; start their runs with project_id=project.id
|
|
228
|
+
|
|
229
|
+
now = datetime.now(timezone.utc)
|
|
230
|
+
month = client.cost.aggregate(
|
|
231
|
+
group_by="month",
|
|
232
|
+
scope_kind="org",
|
|
233
|
+
scope_id=str(org.id),
|
|
234
|
+
from_=now.replace(day=1, hour=0, minute=0, second=0, microsecond=0).isoformat(),
|
|
235
|
+
to=now.isoformat(), # exclusive
|
|
236
|
+
)
|
|
237
|
+
# month.total_usd; month.groups[i].key ({"month": "2026-10"}), .total_usd, .tokens
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Docs: https://docs.kindgi.com/v0.1/guides/observability/cost-per-run/
|
|
241
|
+
|
|
242
|
+
Show it in the app's own UI. **Never:**
|
|
243
|
+
|
|
244
|
+
- **query Kindgi's database**, even on the app's own Postgres server, and
|
|
245
|
+
never map its tables into the app's ORM (SQLAlchemy, Django models). Its
|
|
246
|
+
schema is private and changes with every release (migrations only go
|
|
247
|
+
forward), row-level security guards every tenant query, and a runtime
|
|
248
|
+
Kindgi hosts gives no database access.
|
|
249
|
+
- **link users to Kindgi's console** or any Kindgi UI for this data.
|
|
250
|
+
|
|
251
|
+
To keep a copy (reporting, search), pull it through the API into the app's
|
|
252
|
+
own tables. Docs: https://docs.kindgi.com/v0.1/guides/runs/show-runs-in-your-app/
|
|
253
|
+
|
|
185
254
|
## Build an image
|
|
186
255
|
|
|
187
256
|
`kindgi build --env=<name>` (an `[tool.kindgi.environments.<name>]`
|