orchid-api 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- orchid_api-0.2.0/.editorconfig +22 -0
- orchid_api-0.2.0/.github/workflows/ci.yml +157 -0
- orchid_api-0.2.0/.gitignore +42 -0
- orchid_api-0.2.0/.gitlint +15 -0
- orchid_api-0.2.0/.pre-commit-config.yaml +16 -0
- orchid_api-0.2.0/AGENTS.md +110 -0
- orchid_api-0.2.0/CLAUDE.md +1 -0
- orchid_api-0.2.0/CONTRIBUTING.md +83 -0
- orchid_api-0.2.0/Dockerfile +40 -0
- orchid_api-0.2.0/LICENSE +21 -0
- orchid_api-0.2.0/PKG-INFO +22 -0
- orchid_api-0.2.0/README.md +136 -0
- orchid_api-0.2.0/icon.svg +12 -0
- orchid_api-0.2.0/orchid_api/__init__.py +1 -0
- orchid_api-0.2.0/orchid_api/auth.py +54 -0
- orchid_api-0.2.0/orchid_api/context.py +32 -0
- orchid_api-0.2.0/orchid_api/main.py +149 -0
- orchid_api-0.2.0/orchid_api/models.py +82 -0
- orchid_api-0.2.0/orchid_api/routers/__init__.py +1 -0
- orchid_api-0.2.0/orchid_api/routers/chats.py +85 -0
- orchid_api-0.2.0/orchid_api/routers/legacy.py +93 -0
- orchid_api-0.2.0/orchid_api/routers/messages.py +238 -0
- orchid_api-0.2.0/orchid_api/routers/sharing.py +82 -0
- orchid_api-0.2.0/orchid_api/settings.py +168 -0
- orchid_api-0.2.0/orchid_api/tracing.py +45 -0
- orchid_api-0.2.0/pyproject.toml +63 -0
- orchid_api-0.2.0/tests/conftest.py +58 -0
- orchid_api-0.2.0/tests/test_auth.py +135 -0
- orchid_api-0.2.0/tests/test_context.py +33 -0
- orchid_api-0.2.0/tests/test_models.py +132 -0
- orchid_api-0.2.0/tests/test_routers_chats.py +130 -0
- orchid_api-0.2.0/tests/test_routers_legacy.py +105 -0
- orchid_api-0.2.0/tests/test_routers_messages.py +201 -0
- orchid_api-0.2.0/tests/test_routers_sharing.py +99 -0
- orchid_api-0.2.0/tests/test_settings.py +118 -0
- orchid_api-0.2.0/tests/test_tracing.py +36 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
root = true
|
|
2
|
+
|
|
3
|
+
[*]
|
|
4
|
+
end_of_line = lf
|
|
5
|
+
insert_final_newline = true
|
|
6
|
+
trim_trailing_whitespace = true
|
|
7
|
+
charset = utf-8
|
|
8
|
+
|
|
9
|
+
[*.py]
|
|
10
|
+
indent_style = space
|
|
11
|
+
indent_size = 4
|
|
12
|
+
max_line_length = 120
|
|
13
|
+
|
|
14
|
+
[*.{yml,yaml,json,toml}]
|
|
15
|
+
indent_style = space
|
|
16
|
+
indent_size = 2
|
|
17
|
+
|
|
18
|
+
[Makefile]
|
|
19
|
+
indent_style = tab
|
|
20
|
+
|
|
21
|
+
[*.md]
|
|
22
|
+
trim_trailing_whitespace = false
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# orchid-api (FastAPI server) — CI + Semantic Release
|
|
2
|
+
|
|
3
|
+
name: CI
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
# ── Lint ────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
ruff:
|
|
15
|
+
name: Ruff lint & format
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.13"
|
|
22
|
+
- run: pip install ruff
|
|
23
|
+
- run: ruff check orchid_api/
|
|
24
|
+
- run: ruff format --check orchid_api/
|
|
25
|
+
|
|
26
|
+
commit-lint:
|
|
27
|
+
name: Commit message check
|
|
28
|
+
if: github.event_name == 'pull_request'
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
with:
|
|
33
|
+
fetch-depth: 0
|
|
34
|
+
- uses: actions/setup-python@v5
|
|
35
|
+
with:
|
|
36
|
+
python-version: "3.13"
|
|
37
|
+
- run: pip install gitlint
|
|
38
|
+
- run: gitlint --commits "${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}"
|
|
39
|
+
|
|
40
|
+
# ── Test ────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
test:
|
|
43
|
+
name: Tests + coverage
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
- uses: actions/setup-python@v5
|
|
48
|
+
with:
|
|
49
|
+
python-version: "3.13"
|
|
50
|
+
cache: pip
|
|
51
|
+
cache-dependency-path: pyproject.toml
|
|
52
|
+
- run: pip install -e ".[dev]" pytest-cov
|
|
53
|
+
- name: Run tests
|
|
54
|
+
run: |
|
|
55
|
+
pytest tests/ \
|
|
56
|
+
--cov=orchid_api \
|
|
57
|
+
--cov-report=term-missing \
|
|
58
|
+
--cov-report=xml:coverage.xml \
|
|
59
|
+
--junitxml=report.xml \
|
|
60
|
+
-x
|
|
61
|
+
- name: Upload coverage
|
|
62
|
+
if: always()
|
|
63
|
+
uses: actions/upload-artifact@v4
|
|
64
|
+
with:
|
|
65
|
+
name: coverage
|
|
66
|
+
path: coverage.xml
|
|
67
|
+
if-no-files-found: ignore
|
|
68
|
+
- name: Upload test report
|
|
69
|
+
if: always()
|
|
70
|
+
uses: actions/upload-artifact@v4
|
|
71
|
+
with:
|
|
72
|
+
name: test-report
|
|
73
|
+
path: report.xml
|
|
74
|
+
if-no-files-found: ignore
|
|
75
|
+
- name: Coverage summary
|
|
76
|
+
if: hashFiles('coverage.xml') != ''
|
|
77
|
+
uses: irongut/CodeCoverageSummary@v1.3.0
|
|
78
|
+
with:
|
|
79
|
+
filename: "**/coverage.xml"
|
|
80
|
+
format: markdown
|
|
81
|
+
output: both
|
|
82
|
+
badge: true
|
|
83
|
+
fail_below_min: false
|
|
84
|
+
thresholds: "50 75"
|
|
85
|
+
- name: Add coverage PR comment
|
|
86
|
+
if: github.event_name == 'pull_request' && hashFiles('code-coverage-results.md') != ''
|
|
87
|
+
uses: marocchino/sticky-pull-request-comment@v2
|
|
88
|
+
with:
|
|
89
|
+
header: coverage
|
|
90
|
+
path: code-coverage-results.md
|
|
91
|
+
|
|
92
|
+
# ── Build ───────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
build:
|
|
95
|
+
name: Build wheel
|
|
96
|
+
runs-on: ubuntu-latest
|
|
97
|
+
needs: [ruff, test]
|
|
98
|
+
steps:
|
|
99
|
+
- uses: actions/checkout@v4
|
|
100
|
+
- uses: actions/setup-python@v5
|
|
101
|
+
with:
|
|
102
|
+
python-version: "3.13"
|
|
103
|
+
- run: pip install build
|
|
104
|
+
- run: python -m build
|
|
105
|
+
- uses: actions/upload-artifact@v4
|
|
106
|
+
with:
|
|
107
|
+
name: dist
|
|
108
|
+
path: dist/
|
|
109
|
+
retention-days: 7
|
|
110
|
+
|
|
111
|
+
# ── Release (semantic-release on main push) ─────────────────
|
|
112
|
+
|
|
113
|
+
release:
|
|
114
|
+
name: Semantic release
|
|
115
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
116
|
+
runs-on: ubuntu-latest
|
|
117
|
+
needs: [ruff, test, build]
|
|
118
|
+
permissions:
|
|
119
|
+
contents: write
|
|
120
|
+
id-token: write # required for PyPI trusted publishing
|
|
121
|
+
steps:
|
|
122
|
+
- uses: actions/checkout@v4
|
|
123
|
+
with:
|
|
124
|
+
fetch-depth: 0
|
|
125
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
126
|
+
- uses: actions/setup-python@v5
|
|
127
|
+
with:
|
|
128
|
+
python-version: "3.13"
|
|
129
|
+
- run: pip install python-semantic-release build
|
|
130
|
+
- name: Configure git
|
|
131
|
+
run: |
|
|
132
|
+
git config user.name "github-actions[bot]"
|
|
133
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
134
|
+
- name: Bump version & tag
|
|
135
|
+
id: version
|
|
136
|
+
env:
|
|
137
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
138
|
+
run: |
|
|
139
|
+
OUTPUT=$(semantic-release version --push --tag --commit 2>&1) || true
|
|
140
|
+
echo "$OUTPUT"
|
|
141
|
+
if echo "$OUTPUT" | grep -q "no release will be made"; then
|
|
142
|
+
echo "released=false" >> "$GITHUB_OUTPUT"
|
|
143
|
+
else
|
|
144
|
+
echo "released=true" >> "$GITHUB_OUTPUT"
|
|
145
|
+
fi
|
|
146
|
+
- name: Build distribution
|
|
147
|
+
if: steps.version.outputs.released == 'true'
|
|
148
|
+
run: python -m build
|
|
149
|
+
- name: Publish GitHub Release
|
|
150
|
+
if: steps.version.outputs.released == 'true'
|
|
151
|
+
env:
|
|
152
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
153
|
+
run: semantic-release publish
|
|
154
|
+
# 4. Publish to PyPI via trusted publishing (OIDC)
|
|
155
|
+
- name: Publish to PyPI
|
|
156
|
+
if: steps.version.outputs.released == 'true'
|
|
157
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.egg-info/
|
|
6
|
+
*.egg
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
*.whl
|
|
10
|
+
|
|
11
|
+
# Virtual environments
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
ENV/
|
|
15
|
+
|
|
16
|
+
# IDE
|
|
17
|
+
.idea/
|
|
18
|
+
.vscode/
|
|
19
|
+
*.swp
|
|
20
|
+
*.swo
|
|
21
|
+
*~
|
|
22
|
+
|
|
23
|
+
# Ruff
|
|
24
|
+
.ruff_cache/
|
|
25
|
+
|
|
26
|
+
# pytest
|
|
27
|
+
.pytest_cache/
|
|
28
|
+
htmlcov/
|
|
29
|
+
.coverage
|
|
30
|
+
coverage.xml
|
|
31
|
+
|
|
32
|
+
# Environment
|
|
33
|
+
.env
|
|
34
|
+
.env.local
|
|
35
|
+
.env.*.local
|
|
36
|
+
|
|
37
|
+
# OS
|
|
38
|
+
.DS_Store
|
|
39
|
+
Thumbs.db
|
|
40
|
+
|
|
41
|
+
# mypy
|
|
42
|
+
.mypy_cache/
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Gitlint — enforce Conventional Commits for semantic-release
|
|
2
|
+
# https://jorisroovers.com/gitlint/
|
|
3
|
+
|
|
4
|
+
[general]
|
|
5
|
+
contrib = contrib-title-conventional-commits
|
|
6
|
+
ignore = merge-commit,revert-commit
|
|
7
|
+
|
|
8
|
+
[title-max-length]
|
|
9
|
+
line-length = 72
|
|
10
|
+
|
|
11
|
+
[body-max-line-length]
|
|
12
|
+
line-length = 120
|
|
13
|
+
|
|
14
|
+
[contrib-title-conventional-commits]
|
|
15
|
+
types = feat,fix,perf,refactor,docs,style,test,build,ci,chore
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Pre-commit hooks — run automatically before each commit
|
|
2
|
+
# Install: pip install pre-commit && pre-commit install
|
|
3
|
+
# Manual: pre-commit run --all-files
|
|
4
|
+
|
|
5
|
+
repos:
|
|
6
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
7
|
+
rev: v0.9.10
|
|
8
|
+
hooks:
|
|
9
|
+
- id: ruff
|
|
10
|
+
args: [--fix, --exit-non-zero-on-fix]
|
|
11
|
+
- id: ruff-format
|
|
12
|
+
|
|
13
|
+
- repo: https://github.com/jorisroovers/gitlint
|
|
14
|
+
rev: v0.19.1
|
|
15
|
+
hooks:
|
|
16
|
+
- id: gitlint
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# orchid-api — AI Context
|
|
2
|
+
|
|
3
|
+
## What This Package Is
|
|
4
|
+
|
|
5
|
+
**orchid-api** is the FastAPI server for the Orchid multi-agent AI framework. It imports `orchid` (the library) as a dependency and exposes HTTP endpoints for chat management, message handling, document uploads, and RAG sharing. It does NOT contain agent logic, graph building, or persistence implementations — those live in `orchid/`.
|
|
6
|
+
|
|
7
|
+
## Package Structure
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
orchid-api/
|
|
11
|
+
orchid_api/
|
|
12
|
+
main.py FastAPI app + lifespan (graph build, storage init, tracing)
|
|
13
|
+
settings.py Pydantic BaseSettings + YAML overlay via _apply_yaml_config()
|
|
14
|
+
context.py AppContext dataclass (singleton, populated at startup)
|
|
15
|
+
auth.py Bearer token -> AuthContext via pluggable IdentityResolver (ADR-010)
|
|
16
|
+
models.py Pydantic response models
|
|
17
|
+
tracing.py LangSmith setup
|
|
18
|
+
routers/
|
|
19
|
+
chats.py CRUD: create, list, delete chat sessions
|
|
20
|
+
messages.py Send messages + document upload (multipart/form-data)
|
|
21
|
+
sharing.py Promote chat RAG data to user-common scope
|
|
22
|
+
legacy.py Legacy single-shot /chat endpoint (JSON body)
|
|
23
|
+
Dockerfile
|
|
24
|
+
pyproject.toml
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Key Dependencies
|
|
28
|
+
|
|
29
|
+
| Package | Role |
|
|
30
|
+
|---------|------|
|
|
31
|
+
| `orchid` | Core framework (agents, graph, RAG, persistence) |
|
|
32
|
+
| `fastapi` | HTTP framework |
|
|
33
|
+
| `uvicorn` | ASGI server |
|
|
34
|
+
| `httpx` | Async HTTP client (for identity resolution) |
|
|
35
|
+
| `pydantic-settings` | Environment + YAML config |
|
|
36
|
+
| `python-multipart` | File upload support |
|
|
37
|
+
| `langchain-core` | LangGraph message types |
|
|
38
|
+
|
|
39
|
+
## Architecture Rules (Apply When Editing This Package)
|
|
40
|
+
|
|
41
|
+
1. **This is a thin HTTP layer.** Business logic belongs in `orchid/`, not here. Routers call `orchid` APIs and return responses.
|
|
42
|
+
|
|
43
|
+
2. **Identity resolution happens ONCE in `auth.py`.** The `get_auth_context` dependency resolves the Bearer token into `AuthContext`. No other code initiates OAuth flows (ADR-010).
|
|
44
|
+
|
|
45
|
+
3. **`AppContext` replaces globals.** All runtime state (runtime, graph, chat_repo, http_client, identity_resolver) lives in `context.py:app_ctx`. The `runtime` field is an `OrchidRuntime` instance that owns the reader, LLM service, and MCP client factory. Routers access it via `from ..context import app_ctx`.
|
|
46
|
+
|
|
47
|
+
4. **Routers are split by domain (SRP).** `chats.py` = CRUD, `messages.py` = send + upload, `sharing.py` = share, `legacy.py` = backward compat. New endpoints go in the appropriate router, never in `main.py`.
|
|
48
|
+
|
|
49
|
+
5. **No agent or framework code here.** No `BaseAgent` subclasses, no graph wiring, no RAG logic. Those belong in `orchid/` or consumer projects.
|
|
50
|
+
|
|
51
|
+
6. **Settings priority:** env vars > `orchid.yml` > hardcoded defaults. The `_YAML_TO_ENV` mapping in `settings.py` translates nested YAML keys to flat env vars.
|
|
52
|
+
|
|
53
|
+
7. **Don't persist augmented prompts.** Save the original user message to chat history, NOT the version with prepended file content or RAG context.
|
|
54
|
+
|
|
55
|
+
## Configuration (Settings)
|
|
56
|
+
|
|
57
|
+
All settings are env vars, optionally populated from `orchid.yml` via `ORCHID_CONFIG`:
|
|
58
|
+
|
|
59
|
+
| Setting | Default | Purpose |
|
|
60
|
+
|---------|---------|---------|
|
|
61
|
+
| `LITELLM_MODEL` | `ollama/llama3.2` | LLM model identifier |
|
|
62
|
+
| `AGENTS_CONFIG_PATH` | `agents.yaml` | Path to agent YAML config |
|
|
63
|
+
| `VECTOR_BACKEND` | `qdrant` | Vector store backend |
|
|
64
|
+
| `QDRANT_URL` | `http://qdrant:6333` | Qdrant connection URL |
|
|
65
|
+
| `EMBEDDING_MODEL` | `text-embedding-3-small` | Embedding model |
|
|
66
|
+
| `CHAT_STORAGE_CLASS` | `orchid_ai.persistence.sqlite.SQLiteChatStorage` | Storage backend class |
|
|
67
|
+
| `CHAT_DB_DSN` | `~/.orchid/chats.db` | Database connection string |
|
|
68
|
+
| `DEV_AUTH_BYPASS` | `false` | Skip auth (dev only) |
|
|
69
|
+
| `IDENTITY_RESOLVER_CLASS` | `""` | Dotted path to IdentityResolver |
|
|
70
|
+
| `STARTUP_HOOK` | `""` | Async function called at startup |
|
|
71
|
+
|
|
72
|
+
## Running
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Standalone (no Docker):
|
|
76
|
+
pip install orchid-ai orchid-api
|
|
77
|
+
ORCHID_CONFIG=orchid.yml uvicorn orchid_api.main:app --port 8000
|
|
78
|
+
|
|
79
|
+
# Docker:
|
|
80
|
+
docker build -t orchid-api .
|
|
81
|
+
docker run -p 8000:8000 -v ./orchid.yml:/app/orchid.yml orchid-api
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Endpoints
|
|
85
|
+
|
|
86
|
+
| Method | Path | Router | Purpose |
|
|
87
|
+
|--------|------|--------|---------|
|
|
88
|
+
| POST | `/chats` | chats | Create chat session |
|
|
89
|
+
| GET | `/chats` | chats | List user's chats |
|
|
90
|
+
| DELETE | `/chats/{id}` | chats | Delete chat |
|
|
91
|
+
| GET | `/chats/{id}/messages` | messages | Load chat history |
|
|
92
|
+
| POST | `/chats/{id}/messages` | messages | Send message (multipart) |
|
|
93
|
+
| POST | `/chats/{id}/upload` | messages | Upload documents for chat RAG |
|
|
94
|
+
| POST | `/chats/{id}/share` | sharing | Promote chat RAG to user scope |
|
|
95
|
+
| POST | `/chat` | legacy | Single-shot (no persistence) |
|
|
96
|
+
| GET | `/health` | main | Readiness check |
|
|
97
|
+
|
|
98
|
+
## Code Style
|
|
99
|
+
|
|
100
|
+
- Python 3.11+, Ruff, line length 120
|
|
101
|
+
- `from __future__ import annotations` in every file
|
|
102
|
+
- Imports: `from orchid_ai.xxx` (never `from src.xxx`)
|
|
103
|
+
- No vendor-specific code — platform integrations belong in consumer projects
|
|
104
|
+
|
|
105
|
+
## Common Pitfalls
|
|
106
|
+
|
|
107
|
+
- `POST /chats/{id}/messages` uses `multipart/form-data`, not JSON. The legacy `POST /chat` uses JSON.
|
|
108
|
+
- CORS allows `localhost:3000` and `frontend:3000` — add new origins in `main.py` if needed.
|
|
109
|
+
- The `lifespan()` function builds the graph at startup. Changes to agent config require a restart.
|
|
110
|
+
- Embedding dimension mismatch (768 vs 1536 vs 3072) causes silent retrieval failures. Switching models requires re-indexing.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
AGENTS.md
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Contributing to orchid-api
|
|
2
|
+
|
|
3
|
+
## Development Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
cd orchid-api
|
|
7
|
+
python -m venv .venv
|
|
8
|
+
source .venv/bin/activate
|
|
9
|
+
pip install -e ../orchid -e ".[dev]"
|
|
10
|
+
pre-commit install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The last command installs git hooks that **automatically run ruff (lint + format) and gitlint (commit message check) before every commit**.
|
|
14
|
+
|
|
15
|
+
## Commit Message Convention
|
|
16
|
+
|
|
17
|
+
This project uses **[Conventional Commits](https://www.conventionalcommits.org/)** to enable automatic semantic versioning. Every commit message must follow this format:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
<type>(<scope>): <description>
|
|
21
|
+
|
|
22
|
+
[optional body]
|
|
23
|
+
|
|
24
|
+
[optional footer(s)]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Types
|
|
28
|
+
|
|
29
|
+
| Type | Description | Version Bump |
|
|
30
|
+
|------|-------------|-------------|
|
|
31
|
+
| `feat` | New feature | **minor** (0.X.0) |
|
|
32
|
+
| `fix` | Bug fix | **patch** (0.0.X) |
|
|
33
|
+
| `perf` | Performance improvement | **patch** (0.0.X) |
|
|
34
|
+
| `refactor` | Code refactor (no feature/fix) | none |
|
|
35
|
+
| `docs` | Documentation only | none |
|
|
36
|
+
| `style` | Formatting, whitespace | none |
|
|
37
|
+
| `test` | Adding/updating tests | none |
|
|
38
|
+
| `build` | Build system, dependencies | none |
|
|
39
|
+
| `ci` | CI/CD configuration | none |
|
|
40
|
+
| `chore` | Maintenance tasks | none |
|
|
41
|
+
|
|
42
|
+
### Breaking Changes
|
|
43
|
+
|
|
44
|
+
Append `!` after the type or add `BREAKING CHANGE:` in the footer for a **major** bump:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
feat!: change /chats/{id}/messages response format
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Examples
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
feat(routers): add pagination to GET /chats endpoint
|
|
54
|
+
fix(auth): handle expired bearer tokens gracefully
|
|
55
|
+
docs: update API endpoint table in README
|
|
56
|
+
test(sharing): add tests for scope promotion endpoint
|
|
57
|
+
ci: add coverage reporting to GitLab pipeline
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Validation
|
|
61
|
+
|
|
62
|
+
Commit messages are validated in CI via [gitlint](https://jorisroovers.com/gitlint/). To check locally:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install gitlint
|
|
66
|
+
gitlint
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Running Tests
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pytest tests/ -x # all tests
|
|
73
|
+
pytest tests/ --cov=orchid_api # with coverage
|
|
74
|
+
ruff check orchid_api/ # lint
|
|
75
|
+
ruff format orchid_api/ # format
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Merge Requests
|
|
79
|
+
|
|
80
|
+
1. Create a feature branch from `main`
|
|
81
|
+
2. Use conventional commit messages
|
|
82
|
+
3. Ensure tests pass and linting is clean
|
|
83
|
+
4. Keep MRs focused -- one feature or fix per MR
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# orchid-api — FastAPI + LangGraph agent backend
|
|
2
|
+
#
|
|
3
|
+
# Build context: orchid-api/
|
|
4
|
+
# docker build -t orchid-api .
|
|
5
|
+
#
|
|
6
|
+
# Multi-stage: install deps → slim runtime
|
|
7
|
+
|
|
8
|
+
# ── Stage 1: build & install ───────────────────────────────────
|
|
9
|
+
FROM python:3.13-slim AS builder
|
|
10
|
+
|
|
11
|
+
WORKDIR /app
|
|
12
|
+
|
|
13
|
+
# System deps for building wheels
|
|
14
|
+
RUN apt-get update && apt-get install -y --no-install-recommends gcc && rm -rf /var/lib/apt/lists/*
|
|
15
|
+
|
|
16
|
+
# Install orchid-api (pulls orchid from PyPI as a dependency)
|
|
17
|
+
COPY pyproject.toml pyproject.toml
|
|
18
|
+
COPY orchid_api orchid_api
|
|
19
|
+
RUN pip install --no-cache-dir --prefix=/install .
|
|
20
|
+
|
|
21
|
+
# ── Stage 2: runtime ──────────────────────────────────────────
|
|
22
|
+
FROM python:3.13-slim
|
|
23
|
+
|
|
24
|
+
WORKDIR /app
|
|
25
|
+
|
|
26
|
+
# Copy installed packages from builder
|
|
27
|
+
COPY --from=builder /install /usr/local
|
|
28
|
+
|
|
29
|
+
# Copy application source
|
|
30
|
+
COPY orchid_api orchid_api
|
|
31
|
+
|
|
32
|
+
ENV PYTHONDONTWRITEBYTECODE=1
|
|
33
|
+
ENV PYTHONUNBUFFERED=1
|
|
34
|
+
|
|
35
|
+
EXPOSE 8000
|
|
36
|
+
|
|
37
|
+
HEALTHCHECK --interval=15s --timeout=3s --start-period=10s \
|
|
38
|
+
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1
|
|
39
|
+
|
|
40
|
+
CMD ["uvicorn", "orchid_api.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
orchid_api-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Orchid Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: orchid-api
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Orchid API — FastAPI server for the Orchid agent framework
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: fastapi>=0.115.0
|
|
8
|
+
Requires-Dist: httpx>=0.28.0
|
|
9
|
+
Requires-Dist: langchain-core>=0.3.0
|
|
10
|
+
Requires-Dist: orchid-ai>=1.1.0
|
|
11
|
+
Requires-Dist: pydantic-settings>=2.7.0
|
|
12
|
+
Requires-Dist: pydantic>=2.10.0
|
|
13
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
14
|
+
Requires-Dist: pyyaml>=6.0
|
|
15
|
+
Requires-Dist: uvicorn[standard]>=0.34.0
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: gitlint>=0.19.0; extra == 'dev'
|
|
18
|
+
Requires-Dist: pre-commit>=4.0; extra == 'dev'
|
|
19
|
+
Requires-Dist: pytest-asyncio>=0.25.0; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest-cov>=6.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
22
|
+
Requires-Dist: ruff>=0.9.0; extra == 'dev'
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="icon.svg" alt="Orchid" width="80" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">Orchid API</h1>
|
|
6
|
+
|
|
7
|
+
FastAPI server for the [Orchid](../orchid) multi-agent AI framework.
|
|
8
|
+
|
|
9
|
+
Provides HTTP endpoints for chat management, message handling, document uploads, and RAG sharing. This is a thin HTTP layer -- all agent logic, graph building, and persistence live in the `orchid` library.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Multi-chat session management (create, list, delete)
|
|
14
|
+
- Streaming message send with agent graph invocation
|
|
15
|
+
- File upload with document parsing and chat-scoped RAG
|
|
16
|
+
- Chat sharing (promote RAG data to user scope)
|
|
17
|
+
- Pluggable identity resolution (Bearer token -> AuthContext)
|
|
18
|
+
- LangSmith tracing integration
|
|
19
|
+
- CORS support for frontend clients
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install orchid-api
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Requires the `orchid` library:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install orchid-ai orchid-api
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# With orchid.yml config:
|
|
37
|
+
ORCHID_CONFIG=path/to/orchid.yml uvicorn orchid_api.main:app --port 8000
|
|
38
|
+
|
|
39
|
+
# Health check:
|
|
40
|
+
curl http://localhost:8000/health
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Endpoints
|
|
44
|
+
|
|
45
|
+
| Method | Path | Content-Type | Purpose |
|
|
46
|
+
|--------|------|-------------|---------|
|
|
47
|
+
| `POST` | `/chats` | JSON | Create a chat session |
|
|
48
|
+
| `GET` | `/chats` | -- | List user's chat sessions |
|
|
49
|
+
| `DELETE` | `/chats/{id}` | -- | Delete a chat session |
|
|
50
|
+
| `GET` | `/chats/{id}/messages` | -- | Load chat message history |
|
|
51
|
+
| `POST` | `/chats/{id}/messages` | **multipart/form-data** | Send a message (with optional files) |
|
|
52
|
+
| `POST` | `/chats/{id}/upload` | multipart/form-data | Upload documents for chat RAG |
|
|
53
|
+
| `POST` | `/chats/{id}/share` | -- | Promote chat RAG data to user scope |
|
|
54
|
+
| `POST` | `/chat` | JSON | Legacy single-shot (no persistence) |
|
|
55
|
+
| `GET` | `/health` | -- | Readiness check |
|
|
56
|
+
|
|
57
|
+
## Architecture
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
orchid_api/
|
|
61
|
+
main.py FastAPI app + lifespan (graph build, storage init, tracing)
|
|
62
|
+
settings.py Pydantic BaseSettings + YAML overlay
|
|
63
|
+
context.py AppContext dataclass (singleton, populated at startup)
|
|
64
|
+
auth.py Bearer token -> AuthContext via pluggable IdentityResolver
|
|
65
|
+
models.py Pydantic response models
|
|
66
|
+
tracing.py LangSmith setup
|
|
67
|
+
routers/
|
|
68
|
+
chats.py CRUD: create, list, delete chat sessions
|
|
69
|
+
messages.py Send messages + document upload (multipart/form-data)
|
|
70
|
+
sharing.py Promote chat RAG data to user-common scope
|
|
71
|
+
legacy.py Legacy single-shot /chat endpoint (JSON body)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
All settings are environment variables, optionally populated from `orchid.yml` via `ORCHID_CONFIG`:
|
|
77
|
+
|
|
78
|
+
| Setting | Default | Purpose |
|
|
79
|
+
|---------|---------|---------|
|
|
80
|
+
| `LITELLM_MODEL` | `ollama/llama3.2` | LLM model identifier |
|
|
81
|
+
| `AGENTS_CONFIG_PATH` | `agents.yaml` | Path to agent YAML config |
|
|
82
|
+
| `VECTOR_BACKEND` | `qdrant` | Vector store backend (`qdrant` or `null`) |
|
|
83
|
+
| `QDRANT_URL` | `http://qdrant:6333` | Qdrant connection URL |
|
|
84
|
+
| `EMBEDDING_MODEL` | `text-embedding-3-small` | Embedding model |
|
|
85
|
+
| `CHAT_STORAGE_CLASS` | `orchid_ai.persistence.sqlite.SQLiteChatStorage` | Storage backend class |
|
|
86
|
+
| `CHAT_DB_DSN` | `~/.orchid/chats.db` | Database connection string |
|
|
87
|
+
| `DEV_AUTH_BYPASS` | `false` | Skip auth (dev only) |
|
|
88
|
+
| `IDENTITY_RESOLVER_CLASS` | -- | Dotted path to IdentityResolver subclass |
|
|
89
|
+
| `STARTUP_HOOK` | -- | Async function called at startup |
|
|
90
|
+
| `LANGSMITH_TRACING` | `false` | Enable LangSmith tracing |
|
|
91
|
+
| `LANGSMITH_API_KEY` | -- | LangSmith API key |
|
|
92
|
+
|
|
93
|
+
**Priority:** env vars > `orchid.yml` > hardcoded defaults.
|
|
94
|
+
|
|
95
|
+
## Docker
|
|
96
|
+
|
|
97
|
+
```dockerfile
|
|
98
|
+
# Build (from parent directory):
|
|
99
|
+
docker build -f orchid-api/Dockerfile -t orchid-api .
|
|
100
|
+
|
|
101
|
+
# Run:
|
|
102
|
+
docker run -p 8000:8000 -e ORCHID_CONFIG=/app/config/orchid.yml orchid-api
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Or with docker-compose:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
docker compose -f docker-compose.demo.yml up --build # SQLite
|
|
109
|
+
docker compose -f docker-compose.local.yml up --build # PostgreSQL + Qdrant
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Development
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pip install -e ".[dev]"
|
|
116
|
+
ORCHID_CONFIG=orchid.yml uvicorn orchid_api.main:app --reload --port 8000
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Testing
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pip install -e ".[dev]"
|
|
123
|
+
pytest tests/ -x
|
|
124
|
+
ruff check orchid_api/
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Code Style
|
|
128
|
+
|
|
129
|
+
- Python 3.11+, Ruff, line length 120
|
|
130
|
+
- `from __future__ import annotations` in every file
|
|
131
|
+
- Routers split by domain (SRP): chats, messages, sharing, legacy
|
|
132
|
+
- All runtime state in `AppContext` -- no module-level globals
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT -- see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 24 24" fill="none">
|
|
2
|
+
<rect width="24" height="24" rx="5" fill="#0D0B11"/>
|
|
3
|
+
<!-- Glow -->
|
|
4
|
+
<circle cx="12" cy="12" r="8" fill="#B06AB3" opacity="0.12"/>
|
|
5
|
+
<!-- Orchid petals -->
|
|
6
|
+
<path d="M12 2C10.5 5 9.5 7.5 12 11C14.5 7.5 13.5 5 12 2Z" fill="#D490D7"/>
|
|
7
|
+
<path d="M4 8C6.5 7.5 9 8 11 11C7.5 11.5 5.5 10.5 4 8Z" fill="#C87ECB" opacity="0.9"/>
|
|
8
|
+
<path d="M20 8C17.5 7.5 15 8 13 11C16.5 11.5 18.5 10.5 20 8Z" fill="#C87ECB" opacity="0.9"/>
|
|
9
|
+
<path d="M6 18C7.5 15.5 9.5 13.5 12 13C9 14.5 7 16 6 18Z" fill="#B06AB3" opacity="0.85"/>
|
|
10
|
+
<path d="M18 18C16.5 15.5 14.5 13.5 12 13C15 14.5 17 16 18 18Z" fill="#B06AB3" opacity="0.85"/>
|
|
11
|
+
<circle cx="12" cy="11.5" r="1.5" fill="#FFFFFF"/>
|
|
12
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Orchid API — FastAPI server
|