diff --git a/.github/workflows/sdk-python.yml b/.github/workflows/sdk-python.yml new file mode 100644 index 000000000..43e76dedb --- /dev/null +++ b/.github/workflows/sdk-python.yml @@ -0,0 +1,59 @@ +name: 'SDK Python' + +on: + pull_request: + branches: + - 'main' + - 'release/**' + paths: + - 'packages/sdk-python/**' + - 'docs/developers/sdk-python.md' + - 'docs/developers/_meta.ts' + - 'README.md' + - 'package.json' + - '.github/workflows/sdk-python.yml' + push: + branches: + - 'main' + - 'release/**' + paths: + - 'packages/sdk-python/**' + - 'docs/developers/sdk-python.md' + - 'docs/developers/_meta.ts' + - 'README.md' + - 'package.json' + - '.github/workflows/sdk-python.yml' + +jobs: + sdk-python: + name: 'SDK Python (${{ matrix.python-version }})' + runs-on: 'ubuntu-latest' + strategy: + fail-fast: false + matrix: + python-version: ['3.10', '3.11', '3.12'] + steps: + - name: 'Checkout' + uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 + + - name: 'Set up Python' + uses: 'actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065' # ratchet:actions/setup-python@v5 + with: + python-version: '${{ matrix.python-version }}' + + - name: 'Install SDK test dependencies' + run: | + python -m pip install --upgrade pip + python -m pip install -e 'packages/sdk-python[dev]' + + - name: 'Run Ruff' + run: 'python -m ruff check --config packages/sdk-python/pyproject.toml packages/sdk-python' + + - name: 'Run Ruff Format' + run: 'python -m ruff format --check --config packages/sdk-python/pyproject.toml packages/sdk-python' + + - name: 'Run Mypy' + run: 'python -m mypy --config-file packages/sdk-python/pyproject.toml packages/sdk-python/src' + + - name: 'Run Pytest' + run: 'python -m pytest -c packages/sdk-python/pyproject.toml packages/sdk-python/tests -q' diff --git a/.gitignore b/.gitignore index 9644bc90b..2dae5710a 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ package-lock.json .qoder .claude CLAUDE.md +.codex # Qwen Code Configs .qwen/* @@ -88,3 +89,4 @@ storybook-static # Dev symlink: qc-helper bundled skill docs (created by scripts/dev.js) packages/core/src/skills/bundled/qc-helper/docs +tmp/ \ No newline at end of file diff --git a/README.md b/README.md index b56c72120..5358f9d0e 100644 --- a/README.md +++ b/README.md @@ -424,7 +424,7 @@ As an open-source terminal agent, you can use Qwen Code in four primary ways: 1. Interactive mode (terminal UI) 2. Headless mode (scripts, CI) 3. IDE integration (VS Code, Zed) -4. TypeScript SDK +4. SDKs (TypeScript, Python, Java) #### Interactive mode @@ -452,11 +452,38 @@ Use Qwen Code inside your editor (VS Code, Zed, and JetBrains IDEs): - [Use in Zed](https://qwenlm.github.io/qwen-code-docs/en/users/integration-zed/) - [Use in JetBrains IDEs](https://qwenlm.github.io/qwen-code-docs/en/users/integration-jetbrains/) -#### TypeScript SDK +#### SDKs -Build on top of Qwen Code with the TypeScript SDK: +Build on top of Qwen Code with the available SDKs: -- [Use the Qwen Code SDK](./packages/sdk-typescript/README.md) +- TypeScript: [Use the Qwen Code SDK](./packages/sdk-typescript/README.md) +- Python: [Use the Python SDK](./packages/sdk-python/README.md) +- Java: [Use the Java SDK](./packages/sdk-java/qwencode/README.md) + +Python SDK example: + +```python +import asyncio + +from qwen_code_sdk import is_sdk_result_message, query + + +async def main() -> None: + result = query( + "Summarize the repository layout.", + { + "cwd": "/path/to/project", + "path_to_qwen_executable": "qwen", + }, + ) + + async for message in result: + if is_sdk_result_message(message): + print(message["result"]) + + +asyncio.run(main()) +``` ## Commands & Shortcuts diff --git a/docs/design/openrouter-auth-and-models.md b/docs/design/openrouter-auth-and-models.md new file mode 100644 index 000000000..7c82476a6 --- /dev/null +++ b/docs/design/openrouter-auth-and-models.md @@ -0,0 +1,89 @@ +# OpenRouter Auth and Model Management Design + +This document captures the design intent behind the OpenRouter auth flow and the +model management changes introduced with it. It intentionally focuses on the +product and architectural choices, not implementation history. + +## Goals + +- Let users authenticate with OpenRouter from both CLI and `/auth`. +- Reuse the existing OpenAI-compatible provider path instead of adding a new auth + type for OpenRouter. +- Make the first-run experience usable without asking users to manage hundreds of + models immediately. +- Keep a clear path toward richer model management via `/manage-models`. + +## OpenRouter Auth + +OpenRouter is integrated as an OpenAI-compatible provider: + +- auth type: `AuthType.USE_OPENAI` +- provider settings: `modelProviders.openai` +- API key env var: `OPENROUTER_API_KEY` +- base URL: `https://openrouter.ai/api/v1` + +This avoids introducing an OpenRouter-specific `AuthType` when the runtime model +provider path is already OpenAI-compatible. It keeps auth status, model +resolution, provider selection, and settings schema aligned with the existing +provider abstraction. + +The user-facing flows are: + +- `qwen auth openrouter --key ` for automation or direct API-key setup. +- `qwen auth openrouter` for browser-based OAuth. +- `/auth` → API Key → OpenRouter for the TUI flow. + +Browser OAuth uses OpenRouter's PKCE flow and writes the exchanged API key into +settings before refreshing auth as `AuthType.USE_OPENAI`. + +## Model Management + +OpenRouter exposes a large dynamic model catalog. Writing every discovered model +into `modelProviders.openai` would make `/model` noisy and would turn a long-term +settings field into a cache of a remote catalog. + +The key design split is: + +- **Catalog**: the full set of models discovered from a source such as + OpenRouter. +- **Enabled set**: the smaller set of models that should appear in `/model` and + be persisted in user settings. + +For the initial OpenRouter flow, auth should finish with a useful default enabled +set instead of interrupting the user with a large picker. The recommended set +should be small, stable, and biased toward models that let users try the product +successfully, including free models when available. + +`/model` remains a fast model switcher. It should not become the place where +users browse and curate a full provider catalog. + +## `/manage-models` + +Richer model management belongs in a separate `/manage-models` entry point. That +flow should let users: + +- browse discovered models; +- search by id, display name, provider prefix, and derived tags such as `free` or + `vision`; +- see which models are currently enabled; +- enable or disable models in batches. + +The source dimension must remain part of this design. OpenRouter is only the +first dynamic catalog source; future sources such as ModelScope and ModelStudio +should fit the same shape. UI complexity can be reduced, but the underlying +source abstraction should stay available as the extension point. + +## Current Boundary + +This change should do the minimum needed to make OpenRouter auth and model setup +pleasant: + +- OAuth or key-based auth configures OpenRouter through the existing + OpenAI-compatible provider path. +- The initial enabled model set is curated instead of dumping the full catalog + into settings. +- Full catalog storage, browsing, filtering, and batch management are deferred to + `/manage-models`. + +The design principle is simple: authentication should get users to a working +state quickly, while model curation should live in a dedicated management flow. diff --git a/docs/design/tool-use-summary/tool-use-summary-design.md b/docs/design/tool-use-summary/tool-use-summary-design.md new file mode 100644 index 000000000..f5d88a3e8 --- /dev/null +++ b/docs/design/tool-use-summary/tool-use-summary-design.md @@ -0,0 +1,177 @@ +# Tool-Use Summary Design + +> Fast-model labels for parallel tool batches — motivation, competitive analysis with Claude Code, architecture, and the append-only-Static rationale that drove the current full-mode render. +> +> User documentation: [Tool-Use Summaries](../../users/features/tool-use-summaries.md). + +## 1. Executive Summary + +After each tool batch completes, Qwen Code fires a short fast-model call that returns a git-commit-subject-style label summarizing the batch. The label shows as an inline dim `●