К основному содержанию
скилл CX Codex

python · finom — скилл для Claude Code: типизированный Python-клиент из Vovk API

Скилл генерирует типизированный Python-пакет из схем Vovk.ts: RPC-функции, TypedDict, стриминг через JSON Lines. Нужен, когда API уже есть, а Python-клиент писать руками не хочется.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add finom/vovk --skill python -a claude-code
Открыть на GitHub

Что делает python

Скилл опирается на vovk-python и те же артефакты .vovk-schema/, что и TypeScript-клиент. Один источник правды, два языка. Шаблон py собирает готовый пакет под PyPI: __init__.py с RPC-функциями и TypedDict, api_client.py, py.typed, schema.json, pyproject.toml на hatchling. Шаблон pySrc выдаёт только .py-файлы — их кладут в существующий проект.

Методы статические, имена в snake_case, типы в PascalCase. Порядок позиционных аргументов задаёт генератор: body, files для multipart, query, params, затем всегда headers, api_root и disable_client_validation. Процедуры с iteration дают на клиенте синхронный генератор: next(), list(), выход из цикла через break.

Перед генерацией задайте clientTemplateDefs.py.outputConfig.origin — клиент шлёт запросы на абсолютный URL, и без origin каждый вызов потребует api_root, а vovk generate предупредит. Генератор синхронный, на requests; для asyncio стрим придётся запускать в отдельном потоке. Разовые HTTP-запросы к /api/... этот скилл не покрывает — там хватит curl или httpx.

Какие задачи решает

  • сгенерировать Python-пакет из схем Vovk API
  • получить автодополнение полей в редакторе через TypedDict
  • разложить стриминг JSON Lines на синхронный генератор
  • запечь продовый URL в клиент при генерации
  • собрать пакет под публикацию на PyPI

Примеры запросов агенту

  • Сгенерируй Python-клиент из схемы через vovk-python в ./dist_python
  • Подключи шаблон py в vovk.config.mjs и пропиши origin продового API
  • Сделай стриминг токенов из процедуры complete и собери ответ в список

Как установить python

1Через Skills CLI (Vercel)

npx skills add finom/vovk --skill python -a claude-code

2Вручную: положить файлы

~/.claude/skills/python/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/python/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о python

Как установить скилл python в Claude Code?

Выполните npx skills add finom/vovk --skill python -a claude-code. Файл скилла окажется в ~/.claude/skills/python/SKILL.md.

Что делает скилл python?

Генерирует типизированный Python-пакет из артефактов .vovk-schema/: RPC-функции, TypedDict-типы, HTTP-клиент и файлы сборки. Шаблон pySrc выдаёт только исходники для вставки в существующий проект.

Чем это отличается от обычного вызова API через requests?

Разовые запросы к /api/... проще сделать через curl или httpx — эндпоинты Vovk обычный REST. Скилл нужен, когда требуется сгенерированный типизированный клиент.

Исходный текст

SKILL.md · MIT
---
name: python
description: The Vovk.ts Python client — generating a typed Python package from a Vovk API with `vovk-python`, the `py` / `pySrc` templates, `TypedDict` shapes, JSON + JSON Lines consumption, client-side validation, and PyPI publishing. Use whenever the user wants a typed Python SDK, ongoing Python integration, or PyPI publishing — phrasings like "generate a Python client / SDK", "ship a Python package for my API", "use vovk-python", "pySrc template", "python client streaming", "publish to PyPI". Does NOT cover controller / procedure authoring → hand off to `procedure` skill. Does NOT cover `vovk bundle` in general → hand off to `bundle` skill. Does NOT cover JSON Lines server-side → hand off to `jsonlines` skill. **Does NOT cover one-off HTTP calls** — Vovk endpoints are plain REST, so a `curl`, `httpx`, or `requests` call against `/api/...` is the simpler answer for "I just want to hit my API from a Python script / CLI / shell" use cases. Reach for this skill only when the user actually wants generated typed client code.
---

# Vovk.ts Python client

`vovk-python` generates typed Python package from same `.vovk-schema/` artifacts driving TS client. One source of truth, multi-language.

## Install + generate

```bash
npm i -D vovk-python
```

### Standalone package

Emits complete Python package (PyPI-ready). Canonical output dir `./dist_python` (hello-world convention):

```bash
npx vovk generate --from py --out ./dist_python
```

Output:

```
dist_python/
  src/<package_name>/__init__.py    # RPC functions + TypedDict definitions
  src/<package_name>/api_client.py  # HTTP client
  src/<package_name>/py.typed       # Type-hint marker
  src/<package_name>/schema.json    # Generated schema
  pyproject.toml                    # hatchling build backend
  setup.cfg
  README.md
```

### Embedded source

Emits `.py` files only — drop into existing Python project:

```bash
npx vovk generate --from pySrc --out ./python_src
```

## Auto-generate with composed client

Wire Python into `vovk generate` → runs alongside TS:

```ts
// vovk.config.mjs
const config = {
  composedClient: {
    fromTemplates: ['ts', 'py'],
  },
};
export default config;
```

**Bake prod API URL** via `clientTemplateDefs.py.outputConfig.origin` — generated client uses this by default. Required: client sends calls to an absolute URL, so without an origin every call needs `api_root`, and `vovk generate` warns. Pattern from hello-world:

```ts
// vovk.config.js
const PROD_ORIGIN = 'https://hello-world.vovk.dev';

const config = {
  composedClient: { fromTemplates: ['ts', 'py'] },
  clientTemplateDefs: {
    py: {
      extends: 'py',
      outputConfig: { origin: PROD_ORIGIN },
      // composedClient: { outDir: './dist_python' }, // override output dir if needed
    },
  },
};
```

After this, every `npx vovk generate` (+ every `vovk dev` regen) refreshes Python client — `PROD_ORIGIN` baked in for consumers, overridable per-call via `api_root`.

## Generated call shape

Types follow `[PascalCaseMethodName][Body|Query|Params|Output]` as `TypedDict`. Methods static, snake_case. Positional arg order: the generator only emits slots where validation declared, in this order: `body`, `files` (multipart only, sits between `body` and `query`), `query`, `params`, then always-present trailing kwargs `headers`, `api_root`, `disable_client_validation`. After them, optional: `body` for a POST, PUT or PATCH without a body schema, `query` without a query schema, and `files` for a multipart record body.

```python
from my_api_client import UserRPC  # whatever package the generator wrote

body: UserRPC.UpdateUserBody = {
    "email": "[email protected]",
    "profile": {"name": "John Doe", "age": 25},
}
query: UserRPC.UpdateUserQuery = {"notify": "email"}
params: UserRPC.UpdateUserParams = {
    "id": "123e4567-e89b-12d3-a456-426614174000",
}

# Keyword args work for clarity at call sites:
response = UserRPC.update_user(body=body, query=query, params=params)
```

Method names snake_case. Type names PascalCase. Names alike in snake_case (`getUserByID`, `getUserById`) get the first free suffix, per RPC class in schema order: `get_user_by_id`, `get_user_by_id_2`.

### Multipart uploads via `files`

Procedures with `multipart/form-data` accept `files` kwarg — pass `Dict[str, Any]` matching `requests` upload format:

```python
with open("avatar.png", "rb") as f:
    response = UserRPC.upload_avatar(
        params={"id": user_id},
        files={"avatar": ("avatar.png", f, "image/png")},
    )
```

## JSON Lines streaming

Procedures with `iteration` schemas (server-side `async function*` handlers — see **`jsonlines`** skill) generate **sync Python generators** client-side. Return type `Generator[<MethodName>Iteration, None, None]`. Generator only emits slots for validation declared on procedure — for an iteration-only endpoint with no body / query / params, signature is just trailing kwargs:

```python
@staticmethod
def stream_tokens(
    headers: Optional[Dict[str, str]] = None,
    api_root: Optional[str] = None,
    disable_client_validation: bool = False,
) -> Generator[StreamTokensIteration, None, None]:
    ...
```

Add `body: StreamTokensBody`, `query: StreamTokensQuery`, `params: StreamTokensParams` to the signature only when the procedure declares validation for them (same rule as JSON endpoints).

Yielded items typed against procedure's `iteration` schema — `StreamTokensIteration` is `TypedDict` → editors autocomplete fields.

### Basic consumption

```python
from my_api_client import StreamRPC

for item in StreamRPC.stream_tokens():
    print(item["message"], end="", flush=True)
```

### Standard generator patterns

Sync generators support usual idioms:

```python
# Manual pull
stream = StreamRPC.stream_tokens()
first = next(stream)
second = next(stream)

# Collect into a list (drains the stream)
all_items = list(StreamRPC.stream_tokens())

# Comprehensions
messages = [item["message"] for item in StreamRPC.stream_tokens()]

# Early termination — break out of the for-loop
for item in StreamRPC.stream_tokens():
    if item["message"] == "STOP":
        break
    handle(item)
# The underlying HTTP connection closes when the generator goes out of scope.
```

### With body / query / params

Streaming endpoints accept same input shape as JSON:

```python
from my_api_client import ChatRPC

for token in ChatRPC.complete(
    body={"messages": [{"role": "user", "content": "hello"}]},
    query={"model": "gpt-4"},
):
    print(token["text"], end="", flush=True)
```

### Error handling

HTTP fail or iteration validation fail → generator raises on next `next()`. Wrap in `try/except` to recover:

```python
try:
    for item in StreamRPC.stream_tokens():
        handle(item)
except Exception as exc:
    log.error("stream failed mid-iteration: %s", exc)
```

### Sync, not async

Generator **sync** — backed by `requests`, no async API. For asyncio, run iteration in thread pool (`asyncio.to_thread` or `loop.run_in_executor`) or wrap each `next()` call. Native async streaming not in current Python client.

See **`jsonlines`** skill for server-side handler.

## Client-side validation

Validation runs against `schema.json` by default — same Zod/Valibot shapes server enforces, cross-compiled to JSON Schema.

Skip for hot paths or server-only enforcement:

```python
response = UserRPC.update_user(
    params=params, body=body, query=query,
    disable_client_validation=True,
)
```

## Dependencies pulled in

Generated `pyproject.toml` declares (per hello-world):

```toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
requires-python = ">=3.9"
dependencies = ["requests", "jsonschema[format-nongpl]", "typing_extensions>=4.0.0; python_version < '3.12'"]
```

- `requests` — HTTP client; picks its own `urllib3`.
- `jsonschema[format-nongpl]` — client-side validation; the extra checks formats such as `uri` and `date-time` without GPL libraries. A `pattern` Python's `re` can't compile (`\p{L}`) is left to the server.
- `typing_extensions` — `NotRequired` and `TypeAlias` before Python 3.11.

Build backend **hatchling**, no runtime overhead past deps above.

## Auth + base URL

Generated client targets URL baked at generate time. Override per call via `api_root` kwarg — fully replaces baked-in value for that call. Auth: pass `headers` per call, or set them once on the module-level `client`, whose `session` every call shares:

```py
from my_api import client

client.session.headers["Authorization"] = "Bearer ..."
client.timeout = 30  # seconds, or a (connect, read) tuple; None waits forever
```

**Don't bake secrets into generated package.** Consumers supply API keys at runtime.

## PyPI publishing

Canonical script from hello-world (build wheel + sdist, upload via `twine`):

```bash
python3 -m build ./dist_python --wheel --sdist && python3 -m twine upload ./dist_python/dist/*
```

Both `python3 -m build` and `python3 -m twine` invoke modules directly — works with any Python env that has them installed (`pip install build twine`). Run `twine` with creds via `~/.pypirc` or `TWINE_USERNAME` / `TWINE_PASSWORD` env vars.

Wire into release flow alongside npm bundle + Rust crate (hello-world chains all three under `postversion`):

```json
"scripts": {
  "publish:node":   "npm publish ./dist",
  "publish:rust":   "cargo publish --manifest-path dist_rust/Cargo.toml --allow-dirty",
  "publish:python": "python3 -m build ./dist_python --wheel --sdist && python3 -m twine upload ./dist_python/dist/*",
  "postversion":    "vovk generate && vovk bundle && npm run publish:node && npm run publish:rust && npm run publish:python"
}
```

Version / package name flow from root `package.json` (generator copies whitelisted fields — `name`, `version`, `description`, `license`, `author`, `contributors`, `repository`, `homepage`, `bugs`, `keywords` — into generated `pyproject.toml`). Set `version` in root `package.json`, `npm version patch` propagates to all three targets.

**Name transform — pin before publishing.** Python package name = root `name` with the scope's `@` dropped and every other character Python can't import → underscore (`my-api` → `my_api`, `@acme/web-app` → `acme_web_app`). Same name used for filesystem path (`src/<name>/`), `pyproject.toml` `name`, `pip install` command in README. Set a different distribution name than npm (e.g., npm `my-api`, PyPI `acme_client`) with `outputConfig.package.py_name`:

```js
// vovk.config.mjs
const config = {
  composedClient: { fromTemplates: ['ts', 'py'] },
  clientTemplateDefs: {
    py: {
      extends: 'py',
      outputConfig: {
        origin: 'https://api.example.com',
        package: { py_name: 'acme_client' }, // PyPI name + filesystem path
      },
    },
  },
};
```

## Flows

### "Call my Vovk API from a Python script"

1. `npm i -D vovk-python`.
2. Add `'py'` to `composedClient.fromTemplates` in `vovk.config.mjs`.
3. `npx vovk generate`.
4. `pip install -e ./dist_python` (or wherever generator wrote it).
5. `from vovk_hello_world import UserRPC` and call (editable install exposes the package directly — no `dist_python.src.` prefix).

### "Ship a Python SDK to PyPI"

1. `npx vovk generate --from py --out ./python_package`.
2. Set `version` (and `name`) in the root `package.json` before generating; to override them for this package only, use `clientTemplateDefs.py.outputConfig.package` (see above).
3. `python3 -m build && twine upload dist/*` from `python_package/`.

### "Consume a streaming endpoint from Python"

Server defines `procedure({ iteration })` with `async function*` handler (see `jsonlines`). Client iterates:

```python
for item in StreamRPC.stream_tokens():
    handle(item)
```

### "Embed the client into an existing Python project"

Use `pySrc`:

```bash
npx vovk generate --from pySrc --out ./my_project/vovk_client
```

Then import from `my_project.vovk_client...`.

## Gotchas

- **`TypedDict`, not classes.** Generated shapes dict-based for JSON interop; treat as plain dicts with type hints, not Pydantic models.
- **Method names snake_case**, type names PascalCase. `updateUser` server → `update_user(...)` Python, `UpdateUserBody` etc.
- **Client-side validation uses `jsonschema`** — small import + runtime cost. Disable per-call for speed or server-only enforcement.
- **Base URL override is per-call.** `api_root` kwarg overrides the URL baked at generate time. Headers, timeout and cookies go on the module-level `client` (`client.session.headers`, `client.timeout`).
- **Regen on schema changes.** Any procedure / schema change server-side needs `vovk generate` before Python client reflects. CI should regen as part of build.
- **Don't hand-edit generated files.** Put shared Python utilities alongside, not inside, generated package.
- **Named schemas in `components/schemas` don't yet produce shared importable types.** Each call site gets own scoped `TypedDict` instead of single `UserSchema` reused across methods. Roadmap item — until fixed, expect duplicated type definitions.

© finom · MIT · finom/vovk

Похожие скиллы

Все скиллы по теме «Бэкенд и API» →

Сначала — python от других авторов.

python Mindrally Скилл задаёт агенту стиль Python-кода: PEP 8, type hints, ранние возвраты и async для I/O. Пригодится, когда агент пишет или правит код в проекте. CX Codex +1 983установок python pproenca Скилл с 42 правилами оптимизации Python 3.11+: подсказывает агенту, как писать async-код, выбирать структуры данных и экономить память. CX Codex +1 315установок python siviter-xyz Задаёт агенту единый стиль Python: типизация, внедрение зависимостей, структура тестов и модулей. Пригодится, когда правки идут в существующий проект. CX Codex +1 349установок mcp-builder официальный anthropics Скилл проводит через создание MCP-сервера на TypeScript или Python: от изучения протокола до тестов и оценки качества. 130 949установок claude-api официальный anthropics Справочник по Anthropic SDK и API Claude для генерации кода без устаревших параметров и выдуманных методов. 74 198установок lark-im larksuite Скилл подключает Lark IM к агенту: он отправляет сообщения, ищет историю, управляет чатами и файлами. Подойдёт для задач вокруг командных коммуникаций. CX Codex +1 472 900установок lark-event larksuite Слушает события Lark/Feishu и отдаёт их потоком NDJSON. Подходит для ботов, обработчиков сообщений и долгих подписчиков. CX Codex +1 466 800установок caveman-discover JuliusBrussee Находит точки вызова LLM в репозитории и размечает их воркфлоу для Caveman Cloud, чтобы расходы на токены не сливались в одну общую кучу. CX Codex +1 104 400установок migration JuliusBrussee Скилл задаёт правила безопасных миграций схем, данных и API с обязательным откатом. Пригодится при изменениях, где нельзя терять данные или ломать совместимость. CX Codex +1 103 900установок

Из того же набора5

Спонсоры проекта

Компании, которые помогают держать аналитику открытой