wikai
v0.4.0
Published
TypeScript SDK for the wikai API. Zero runtime dependencies, uses native `fetch` (Node.js 18+).
Readme
@constructory/wikai-sdk
TypeScript SDK for the wikai API. Zero runtime dependencies, uses native fetch (Node.js 18+).
Installation
npm install @constructory/wikai-sdkQuick start
import { WikiaiClient } from '@constructory/wikai-sdk'
const client = new WikiaiClient({
baseUrl: 'https://your-wikai-service.com',
apiKey: 'wk_live_...',
})
// Ingest knowledge via tagged markdown
const result = await client.ingest({
markdown: `
[concept description="Recovery of previously paid compensation"]
Clawback
[alias]compensation recovery[/alias]
[alias priority=10]clawback provision[/alias]
[supported_by]
[source url="https://example.com/sec-rules"]
SEC Compensation Recovery Rules
[/source]
[/supported_by]
[/concept]
`,
})
// Search
const hits = await client.search({ query: 'clawback' })
// Inspect a single entity
const detail = await client.inspect({ entityType: 'concept', entityKey: 'clawback' })
// Delete an entity
await client.entities.delete('concept', 'outdated-concept')Client
const client = new WikiaiClient({
baseUrl: string, // API endpoint
apiKey: string, // Bearer token (wk_live_... or wk_test_...)
maxRetries?: number, // Retry count for 5xx/429 (default: 2)
})Retries use exponential backoff (1s, 2s, 4s, capped at 10s).
API
Ingest
The single write path. Accepts tagged markdown and returns created/updated entities and edges.
const result = await client.ingest({ markdown: '...' })
// result.entities — IngestedEntity[]
// result.edges — IngestedEdge[]
// result.errors — ParseError[] (if empty, ingest succeeded)Entities are identified by type + key (derived from the tagged markdown). Re-ingesting with the update flag updates existing entities. The replace flag on update removes outgoing edges not in the block.
Aliases are managed via [alias] tags inside entity blocks. See the tagged markdown spec for full syntax.
Search
const result = await client.search({
query: 'throttling',
filters: { vertical: 'saas' }, // optional property filters
limit: 10, // optional
})
for (const hit of result.hits) {
console.log(hit.displayName, hit.score, hit.matchedOn)
for (const linked of hit.linkedEntities) {
console.log(` -> ${linked.displayName} via ${linked.edgeType}`)
}
}SearchResult:
| Field | Type | Description |
|-------|------|-------------|
| query | string | Original query |
| expandedQueries | string[] | Alias-expanded variants |
| hits | SearchHit[] | Ranked results |
SearchHit:
| Field | Type | Description |
|-------|------|-------------|
| type | string | Entity type |
| entityKey | string | Entity key |
| displayName | string | Display name |
| score | number | Relevance score |
| matchedOn | MatchSource[] | How the hit was found |
| properties | object | Entity properties |
| linkedEntities | LinkedEntity[] | Connected entities |
MatchSource: 'name' | 'alias' | 'edge' | 'field' | 'full_text' | 'body'
Inspect
Returns full detail for a single entity.
const detail = await client.inspect({
entityType: 'concept',
entityKey: 'rate-limiting',
})
if (detail) {
detail.aliases // { aliasValue, scope, priority }[]
detail.edges // { direction, edgeType, entityType, entityKey, displayName, properties }[]
detail.properties
detail.body
}Returns null if the entity does not exist.
Entities
// Get by type + key
const entity = await client.entities.get('concept', 'rate-limiting')
// List all of a type
const concepts = await client.entities.list('concept')
// Delete (soft archive)
const { deleted } = await client.entities.delete('concept', 'rate-limiting')Deleted entities are hidden from get, list, and search. Re-ingesting with the update flag restores them.
Entity:
| Field | Type | Description |
|-------|------|-------------|
| id | string | UUID |
| entityType | string | Type |
| entityKey | string | Key |
| displayName | string | Display name |
| body | string | null | Body content |
| status | string | 'active' or 'archived' |
| version | number | Current version |
| properties | object | JSONB properties |
| createdAt | string | ISO timestamp |
| updatedAt | string | ISO timestamp |
Edges
const edges = await client.edges.between(entityId)Returns all edges where the entity is either the source or target.
Admin
// Create API key
const key = await client.admin.createKey({
name: 'production',
environment: 'live',
})
console.log(key.plainKey) // shown only once
// List keys
const keys = await client.admin.listKeys()
// Revoke
await client.admin.revokeKey(keyId)Error handling
All errors extend WikiaiError:
import {
WikiaiError,
NotFoundError,
ValidationError,
ConflictError,
AuthenticationError,
RateLimitError,
} from '@constructory/wikai-sdk'| Error | Status | When |
|-------|--------|------|
| ValidationError | 400 | Invalid input |
| AuthenticationError | 401 | Bad/missing API key |
| NotFoundError | 404 | Resource not found |
| ConflictError | 409 | Duplicate key |
| RateLimitError | 429 | Rate limited (retried automatically) |
| WikiaiError | 5xx | Server error (retried automatically) |
ValidationError, NotFoundError, and ConflictError have a fields property with per-field details when available.
Types
All types are exported from the package root:
import type {
IngestInput, IngestResult, IngestedEntity, IngestedEdge, IngestedAlias, ParseError,
SearchInput, SearchResult, SearchHit, MatchSource, LinkedEntity,
InspectInput, InspectResult,
Entity, Edge,
CreateApiKeyInput, ApiKeyResponse, ApiKeyListItem,
} from '@constructory/wikai-sdk'