KJDRAW / CONNECT YOUR MODEL
Connect your model
Bring your model, gateway or agent framework. Keep one CAD engine and one set of drawing tools.
Choose a connection#
KJDraw does not require a particular AI vendor. Choose a protocol adapter, supply your model and host transport, and run the same CAD tools. A custom KJAgentModel connects frameworks, local models or other protocols without changing the drawing engine.
| Connection | Adapter value | Host transport |
|---|---|---|
| OpenAI Responses | responses | Responses REST endpoint |
| Chat Completions-compatible services | chat-completions | The selected service's compatible endpoint; DeepSeek is one evaluation option |
| Claude Messages | anthropic-messages | Messages REST endpoint |
| Gemini GenerateContent | gemini-generate-content | GenerateContent REST endpoint, with model in the URL |
| Your framework, gateway or local model | Custom KJAgentModel | Your own conversation bridge |
The built-in adapters support the protocols listed above. Compatibility with a particular model or vendor extension depends on that endpoint's function-calling behavior. For text-only models, a custom bridge can parse and validate a structured response; never execute model-generated JavaScript.
Wire the model once#
Install KJDraw, then import the model adapter and bounded task runner from their public package entries:
import { createKJDrawSDK } from '@kanjieteam/kjdraw'
import { KJAgentToolSession } from '@kanjieteam/kjdraw/agent-tools'
import { createKJModelAdapter } from '@kanjieteam/kjdraw/model-adapters'
import { runKJAgentTask } from '@kanjieteam/kjdraw/agent-runner'
const sdk = createKJDrawSDK()
const drawing = sdk.createDocument({ units: 'millimeter' })
const session = new KJAgentToolSession(sdk, drawing)
const model = createKJModelAdapter({
protocol: 'chat-completions',
model: selectedModel,
request: ({ body, signal }) => hostModelGateway(body, signal),
})
const result = await runKJAgentTask({
session,
model,
prompt: 'Propose a circle at (20, 25) mm with a radius of 3 mm.',
toolNames: ['cad_read_drawing', 'cad_propose_circles'],
maxTurns: 8,
maxToolCalls: 32,
})
selectedModel and hostModelGateway belong to your application. The gateway returns parsed, non-streaming provider JSON and rejects HTTP failures. For Gemini, send the body to the configured model URL; this is a REST adapter, not the Google SDK's nested config argument. Keep keys, endpoint allowlists and user permissions on your server. The CAD package does not discover keys, choose an endpoint or send network requests by itself.
Chat-compatible endpoints differ in output token fields: the default is max_tokens; set chatTokenParameter: 'max_completion_tokens' when required. The other adapters map maxOutputTokens to their protocol. Full runtime argument validation remains enabled; Responses explicitly uses non-strict tool generation rather than promising identical provider-side schema support.
Choose tools for a task#
toolNames is an optional host policy for one run. Omit it to retain all session tools. Supply a nonempty list of unique exact names from session.definitions; unknown names, duplicates and empty lists fail before opening a model conversation. Definitions retain their canonical order and complete schemas, including drawing units. The runner snapshots the selection before invoking the model, so later array changes cannot widen access.
Every adapter and custom bridge receives the same selected definitions. If a response requests an omitted tool, the runner returns failed with KJAGENT_TOOL_NOT_ALLOWED before dispatching any call in that batch. The next run selects its own policy. This does not restrict trusted host calls made directly on the session, replace authentication, or allow the model to approve proposals. Include reading and pagination tools when the task needs them. Smaller schemas reduce JSON bytes; actual model token counts and task success still require provider measurements.
Review the result#
| Status | What the host should do |
|---|---|
awaiting-approval | Display the exact proposals in outputs; use proposalIds to approve or reject from your review UI |
responded | Show the model's answer or clarification; it is not evidence that a drawing task succeeded |
limit-reached | Inspect results and adjust the task or budget; do not automatically retry forever |
cancelled | The host cancelled or the run timed out; late model calls are not dispatched |
failed | Handle the structured error; inspect private transport diagnostics on the server |
The runner stops when it has proposals. It never calls approve(). After an authenticated user reviews the exact arguments, your host calls session.approve(planId, user.id) or session.reject(planId, user.id) and checks the result. Model text must be displayed as untrusted text, not unsanitized HTML.
Each run starts from the drawing's current revision. After applying a proposal, submit the next request as a new run so the model reads the updated geometry. Your application owns conversation persistence and save policy. See Agent workflows for CAD tools, proposal previews and geometry checks.
To expose the same tool registry to an MCP client, run the packaged local stdio host with paths chosen by your application:
npx --package @kanjieteam/kjdraw kjdraw-mcp --workspace ./project --input drawing.kjd --proposals pending.json
The host reads the selected KJD or DXF and exposes drawing queries and proposals. It never approves a proposal or writes the input drawing; your application reviews and applies accepted work. The pending-proposal file is created exclusively, so an existing file is rejected instead of overwritten.
Measure tokens and time#
result.measurements retains each attempted turn's usage, normalized totals, transport wall time and runner wall time. Counters come from provider response fields; missing or invalid counters remain null. Cache and reasoning counters are subsets or additional components according to each protocol, so they are not blindly added twice. A cancelled request without a received response has missing usage. complete describes observed token counters, not design completion or a billing receipt.
const { totals, transportWallMs, runWallMs, complete } = result.measurements
// Keep null as unavailable; do not replace it with zero in a benchmark.
console.log({ totals, transportWallMs, runWallMs, complete })
Adapters also accept onUsage: usage => hostMetrics.record(usage) for response observations, including rejected or truncated responses. Observer failures do not change drawing behavior. A late response after cancellation may still reach that host observer; it cannot rewrite the runner's returned measurement snapshot. extractKJModelUsage is available from /model-usage for a custom transport. No response text, credentials, inferred token counts or prices are included. Controlled live-model comparisons still require repeated matched tasks and actual provider configuration.
Run the packaged example#
node node_modules/@kanjieteam/kjdraw/examples/model-agent.mjs
By default, the example runs offline and demonstrates all four wire formats, host approval, saved geometry and undo without contacting a model.
To connect an online model, configure KJDRAW_MODEL_PROTOCOL, KJDRAW_MODEL_NAME, KJDRAW_MODEL_ENDPOINT and KJDRAW_MODEL_API_KEY in a trusted server or CLI environment, then add --live. The endpoint is the complete trusted REST URL. The example rejects redirects and URL credentials, limits response bytes and never applies live proposals automatically. It sends at most four model requests and eight tool calls; provider charges may apply.
Extend and validate#
Implement KJAgentModel.createConversation({ instructions, tools }) and return next(input, signal). A turn returns text plus calls containing id, name and arguments. Inputs are either the initial prompt or ordered tool results. Keep vendor continuation data private to that conversation and preserve call/result IDs. Existing Agent runtimes can also use KJAgentToolSession directly and manage their own execution loop.
Adapters retain Responses reasoning items, chat reasoning fields, Claude signed thinking blocks and Gemini thought signatures in their original conversation. They do not place those fields in the public answer. Truncated, blocked, malformed or unsupported responses stop before tool dispatch. Tool validation errors can be returned to the model for bounded correction. Timeouts cannot stop a transport that ignores its signal from consuming remote resources; the host must enforce its own network and billing limits.
Protocol references: OpenAI function calling, DeepSeek tool calls, Claude tool results, Gemini GenerateContent.