diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..de80bc2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,60 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project overview + +Plyngent is an LLM chat and agent toolkit (Python 3.14+, PDM-managed). It is in early development — core protocol clients exist, while agent, CLI, web, memory, and config modules are planned but not yet implemented. + +## Commands + +```bash +pdm install # first-time dependency setup +pdm sync # sync after pulling changes + +pdm run basedpyright . # type checking (basedpyright, "recommended" strictness) +pdm run ruff check . # linting +pdm run ruff format . # formatting +``` + +There is no test runner configured yet. + +## Architecture + +### Data modeling: `msgspec.Struct` + +All protocol models use `msgspec.Struct` — not dataclasses, not Pydantic. This gives type-safe, schema-validated structs with efficient JSON codec support. Use `msgspec.field(default=UNSET)` for optional fields to distinguish "not provided" from `None`. + +The `Unset` type alias (`typedef.py`) wraps `msgspec.UNSET` as `Literal[UnsetType.UNSET]`. `JSONSchema` is `dict[str, Any]`. + +### Protocol layering (`lmproto/`) + +``` +lmproto/openai_compatible/ ← Base: model, config, client +lmproto/deepseek/openai_compat/ ← Extends base via inheritance + extra fields +``` + +- **`openai_compatible/model.py`** — chat message models (`SystemChatMessage`, `UserChatMessage`, `AssistantChatMessage`, `ToolChatMessage`), tool definitions, request/response structs, streaming chunks. All `msgspec.Struct` with `rename="snake"` for camelCase JSON interop. +- **`openai_compatible/client.py`** — `BaseOpenAIClient` using `niquests` for async HTTP with SSE streaming. Two response modes: `ChatCompletionResponse` (non-streaming) and `AsyncIterator[ChatCompletionChunk]` (streaming). +- **`openai_compatible/config.py`** — `OpenAIConfig` dataclass (base URL, API key, model name, max tokens, temperature). +- **DeepSeek extension** — `DeepseekOpenAIClient` extends `BaseOpenAIClient`. Models add `reasoning_content`, `prefix`, `ThinkingOptions`, and `DeepSeekReasoningEffort` (including `"max"`). + +### Composition utility: `Forward` descriptor + +`utils/components.py` provides a `Forward[T]` descriptor for forwarding attribute access to a composed sub-object — prefer composition over inheritance where appropriate. The `forward()` factory function constructs `Forward` instances with type inference. + +### Type annotations are mandatory + +Basedpyright in `recommended` mode (strictest preset). Ruff lint rules include `ANN` (flake8-annotations) — all functions must have type annotations except private functions (`ANN202` suppressed). Use PEP 695 `type X = ...` syntax for type aliases. Python 3.14+ generics syntax is expected. + +## Commit messages + +Scoped commit messages, not conventional commits: + +``` +: +``` + +Examples: `deps: add fastapi`, `core/mq: fix incorrect message sending`, `router: add routing service for xxx`, `test/webserver: change test client`, `ci/lint: run ruff style check`. + +Check `git log` for the full convention. diff --git a/doc/architecture.md b/doc/architecture.md new file mode 100644 index 0000000..d707a2d --- /dev/null +++ b/doc/architecture.md @@ -0,0 +1,29 @@ +# Code Architecture + +- plyngent + - typedef: Shared type aliases. + - lmproto: Protocols for interacting with LLM service. + - (common) + - model: Messages models of specified providers. + - config: Configurations for specified providers. + - client: Clients for accessing existent specified services. + - server: Servers for accepting other clients. + - openai + - openai_compatible + - anthropic + - ollama + - deepseek + - openai_compat + - anthropic + - utils: Common utilities for code architecture. + - components: Utilities for class composition. + - memory: Storage controlling for sessions and messages. + - router: Multi-source capability routing and interception. + - interceptors: Capability of stealing a few arguments for partial re-routing. + - ... + - config: Plyngent configuration center. + - agent: Agent capabilities and controlling. + - tools: Client code of tool calls. + - cli: CLI for chat/agent application. + - web: Web service for chat/agent application. + diff --git a/doc/development-principles.md b/doc/development-principles.md new file mode 100644 index 0000000..96fe01f --- /dev/null +++ b/doc/development-principles.md @@ -0,0 +1,60 @@ +# Development Principles + +## If you are new to project + +### Have a general overview of the project + +You should at least have a clear view of the general architecture. This aims for: + +1. keeping the style of codebase consistent. +2. using or extending existing objects or functions instead of writing new but duplicate code, since the latter is usually a kind of **SHIT**. + +For a general architecture reference, see [here](./architecture.md). + +This does not mean you should immediately grasp the whole codebase, but **you should not make changes until you have a clear view of the general architecture**. + +### Commit messages + +This project uses scoped commit message. Examples: + +- `project: init`: initializing a new project. +- `deps: add fastapi`: add/upgrade/removing a dependency. +- `deps/dev: upgrade ruff`: add/upgrade/removing a dev dependency. +- `core/mq: fix incorrect message sending`, `router: add routing service for xxx`: add/change/fix/refactor/removing functional code. +- `test/webserver: change test client to niquests`: add/change/fix/refactor/removing tests. +- `scripts/i18n: implement dumping translation keys`: add/change/fix/refactor/removing general scripts. +- `ci/lint: run ruff style check`, `ci/build: fix changelog generation`: add/change/fix/refactor/removing CI workflows. + +Once conventional commit was good, until redundant forms like `fix: fix ...`, `refactor: refactor ...` came up. So we use scoped commit message here. + +If you are still not quite familiar to this, check the git log in any way you prefer. + +## General information and suggestions + +### Local development environment + +Syncing packages: `pdm install` the first time, then `pdm sync`. The former can fully initialize the dependencies, and the latter can continuously keep consistent dependencies. + +For other commands like adding or removing packages, see [PDM Documentation](https://pdm-project.org/en/latest/usage/dependency/). These commands usually updates lock file automatically by default, so `pdm sync` should only be used when after `git pull/fetch/checkout`. + +### Type checking and linting + +Firstly, we prefer using `pdm run ` to directly running commands in venv (whether in activated environment or not). + +Then for every projects, check the dev dependency group where you can find tools used for type checking and linting. + +Type checking: `pdm run basedpyright .` +Linting: `pdm run ruff check .` +Formating: `pdm run ruff format .` + +