Files
deepseek-harness/packages/llm/README.md
T
Tianyi Cui 7f024a1a9d Document the codebase thoroughly and tighten type safety
Docs: per-folder README.md for packages/ (family overview + one per
package: service, events, API, extension points, TODOs), examples/,
and examples/echo-agent/; folder-level AGENTS.md (+ CLAUDE.md
symlinks) for packages/ and vendor/; module-level doc comments in
every packages/*/src file; richer JSDoc on all exported API
(event side effects, disposal contracts, error behavior). Root
AGENTS.md gains a "Type Safety and Documentation" policy section:
the codebase aims to be very type-safe and well documented; type
gymnastics are acceptable in core packages when they improve
plugin-author DX; verbose docs are fine as long as they stay strictly
in sync with the code.

Type safety: removed the upstream-inherited "noImplicitAny": false
from tsconfig.base.json — packages/* now compile under full strict
mode; vendor/loader and vendor/include set it locally (vendor/cordis
already did). Eliminated every `: any` / `as any` from packages and
examples (catch clauses use unknown + a CodedError narrowing type;
event data access uses discriminated-union narrowing).

Typed tool schemas: new @deepseek-ai/dsh-tools schema DSL —
SchemaSpec with per-property `required: true` booleans, type-level
InferArgs<S>, a runtime SchemaSpec → JSON Schema converter, and
defineTool() so first-party tools get typed execute(args) with zero
casts (raw JSON Schema still accepted for MCP interop; chosen over
schemastery because it targets JSON Schema generation directly).
echo-tool and all test tools migrated; +7 tests.
2026-06-11 13:01:00 +08:00

65 lines
2.7 KiB
Markdown

# dsh-llm
Provider-neutral LLM vocabulary and abstract service. This package defines the
canonical language spoken by the agent loop, session logs, and every plugin.
## Service: `LlmService` (ctx key: `llm`)
An adapter registry plus streaming / non-streaming call surfaces. Both call
surfaces are interceptable via waterfall events.
### Public API
- `ctx.llm.registerAdapter(models: string[], adapter: LlmAdapter): () => void`
Register an adapter for the given model names. Disposed with the calling fiber.
- `ctx.llm.models(): string[]` — model names with a registered adapter.
- `ctx.llm.stream(options: GenerateOptions): AsyncIterable<StreamChunk>`
Stream one model call as raw chunks (token-level deltas).
- `ctx.llm.streamBlocks(options: GenerateOptions): AsyncIterable<ContentBlock>`
Stream as completed content blocks (convenience view).
- `ctx.llm.generate(options: GenerateOptions): Promise<GenerateResult>`
One model call, fully assembled.
### Events
| Event | Mode | Purpose |
|---|---|---|
| `llm/stream` | waterfall | Intercept/wrap every streaming model call (retry, caching, routing) |
| `llm/generate` | waterfall | Intercept/wrap every non-streaming model call |
| `llm/adapter-change` | emit | An adapter was registered or unregistered |
### Extension points
- Subclass `LlmAdapter` and call `ctx.llm.registerAdapter(models, adapter)`
to add a new model provider.
- Wrap `llm/stream` or `llm/generate` via `ctx.on()` waterfall listeners for
caching, retry, logging, rate-limiting, etc.
### Content-block vocabulary (`types.ts`)
Messages are arrays of typed content blocks: `text`, `reasoning`, `tool-call`,
`tool-result`, `image`. The union is derived from the merge-extensible
`ContentBlockMap`, so plugins can add block types via declaration merging.
Streaming is a raw chunk protocol (`block-start`, `text-delta`,
`reasoning-delta`, `tool-call-delta`, `block-end`, `usage`, `finish`).
`BlockAssembler` is the single shared implementation that assembles chunks into
blocks/messages.
### Classes
- `LlmAdapter` — abstract base class for provider adapters. The only required
method is `stream()`.
- `BlockAssembler` — incrementally assembles raw chunks into complete content
blocks and an assistant message. Used by the agent loop (raw chunks for replay
+ assembled for history) and by `streamBlocks()`/`generate()`.
- `LlmError` — typed error with a `code` string (`NO_ADAPTER`,
`DUPLICATE_ADAPTER`).
### What is NOT here (TODO)
- **DeepSeek V4 adapter** — the first real adapter lands in a later phase.
- **Streaming protocol review** — the chunk protocol has `TODO(review)` markers
and needs careful review before the first real adapter (DeepSeek V4 wire
format, partial JSON arguments, interleaved reasoning signatures, ...).