|
| 1 | +--- |
| 2 | +"@objectstack/client": major |
| 3 | +"@objectstack/spec": major |
| 4 | +--- |
| 5 | + |
| 6 | +feat(client,spec)!: the SDK's `ai` namespace now expresses the AI surface that exists (#3718) |
| 7 | + |
| 8 | +`client.ai` and the AI service were **disjoint sets**. The namespace held three |
| 9 | +methods — `nlq`, `suggest`, `insights` — whose URLs no repo has ever mounted |
| 10 | +(removed in v17), while `service-ai` mounted 12 routes the SDK could not reach |
| 11 | +at all. v17 closed the first half by deleting the dead methods. This closes the |
| 12 | +second: the SDK now reaches every route that is meant to be tenant API surface. |
| 13 | + |
| 14 | +| SDK | Route | |
| 15 | +|---|---| |
| 16 | +| `ai.chat(request)` | `POST /api/v1/ai/chat` — forces `stream: false`, so the JSON mode is what you get | |
| 17 | +| `ai.chatStream(request)` | `POST /api/v1/ai/chat` — `AsyncIterable` of UI Message Stream frames | |
| 18 | +| `ai.complete(request)` | `POST /api/v1/ai/complete` | |
| 19 | +| `ai.models()` | `GET /api/v1/ai/models` — the ADR-0028 plan-filtered picker list | |
| 20 | +| `ai.conversations.create/list/get/update/delete/addMessage` | the six `/api/v1/ai/conversations` routes | |
| 21 | + |
| 22 | +`ai.chatStream` returns a promise for an async iterable rather than being an |
| 23 | +async generator, so the request is issued — and an HTTP error thrown — when you |
| 24 | +call it, not when you first iterate. |
| 25 | + |
| 26 | +**Where the server is.** `service-ai` is a Cloud/EE package in the `cloud` |
| 27 | +repo; this repo only proxies `/api/v1/ai/**` and 404s `AI service is not |
| 28 | +configured` without it. Check `discovery.services` before calling, exactly as |
| 29 | +for any other plugin-provided namespace. For a React chat UI, `useChat()` |
| 30 | +(`@ai-sdk/react`) is still the better client — it speaks the same protocol |
| 31 | +`ai.chatStream` parses and owns message state; these methods are for callers |
| 32 | +that are not components. |
| 33 | + |
| 34 | +**Breaking — the spec's dead AI declarations are retired.** All three had no |
| 35 | +implementation anywhere and no runtime consumer: |
| 36 | + |
| 37 | +- `Ai{Nlq,Suggest,Insights}{Request,Response}[Schema]` → replaced by the wire |
| 38 | + shapes of the real routes: `AiChat{Request,Response}`, `AiStreamChunk`, |
| 39 | + `AiCompleteRequest`, `AiModelsResponse`, `AiConversation`, `AiMessage`, |
| 40 | + `{Create,List,Update}AiConversation*`. The six retired JSON Schemas are |
| 41 | + dropped from `json-schema.manifest.json` (deliberate retirement, #2978). |
| 42 | +- `DEFAULT_AI_ROUTES` → deleted, and `getDefaultRouteRegistrations()` returns 8 |
| 43 | + groups instead of 9. It declared the three phantom endpoints and had no |
| 44 | + runtime consumer; re-declaring the real ones here would recreate the same |
| 45 | + illusion, since they are mounted from another repo. |
| 46 | +- `AiProtocol` (`aiNlq?` / `aiSuggest?` / `aiInsights?`) → deleted. Nothing |
| 47 | + implemented it and nothing dispatched through it. The real server contract is |
| 48 | + `IAIService` + `IAIConversationService` in `@objectstack/spec/contracts`. |
| 49 | + |
| 50 | +**The guard.** `/api/v1/ai/` becomes a bounded prefix exemption in the capstone |
| 51 | +(#3642) alongside the control plane — bounded from both ends: only `ai.*` may |
| 52 | +use it, and the namespace must still be reaching it. That is not a |
| 53 | +wave-through. The reachability check lives where the routes are: |
| 54 | +`cloud`'s `packages/service-ai/src/ai-route-ledger.conformance.test.ts` reads |
| 55 | +the table `buildAIRoutes()` returns and drives this SDK against it, so an |
| 56 | +`ai.*` URL that stops resolving fails a test in the repo that mounts it. The |
| 57 | +wildcard-only bound stays **0** — these URLs never touch the `* /ai/**` row, |
| 58 | +which is what certified three dead methods for years. |
| 59 | + |
| 60 | +The four replaced client tests are worth naming: they mocked `fetch` and |
| 61 | +asserted the URL the client *built*, never that anything answered it, and |
| 62 | +passed for years against endpoints that did not exist. The new ones assert only |
| 63 | +what this repo can honestly know — verb, path, and the body decisions the SDK |
| 64 | +makes for you (`stream: false` on `chat`, the 204 on `delete`, SSE frame |
| 65 | +parsing) — and leave "does it resolve" to the ledger next to the routes. |
0 commit comments