Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 34 additions & 16 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CLAUDE.md

Этот файл содержит контекст для Claude Code при работе с проектом.
Контекст для Claude Code при работе с проектом.

## О проекте

Expand Down Expand Up @@ -36,22 +36,35 @@ app/
│ ├── config.py # Pydantic Settings
│ ├── db.py # SQLModel database
│ ├── health.py # Health endpoint
│ └── llm_client.py # OpenRouter client
├── models/ # SQLModel entities
├── schemas/ # API request/response
├── services/ # ChatService
├── integrations/ # Notion, BehaviorManager
├── behavior/ # Behavior models
└── observability/ # OpenTelemetry tracing
│ ├── llm_client.py # OpenRouter client
│ └── project_memory.py # In-memory fallback
├── models/
│ ├── chat.py # Chat models
│ └── db_models.py # SQLModel entities
├── schemas/
│ ├── chat.py # Chat request/response
│ └── projects.py # Project schemas
├── services/
│ └── chat_service.py # ChatService бизнес-логика
├── integrations/
│ ├── notion_client.py # Notion API client
│ └── behavior_manager.py # Behavior loading
├── behavior/
│ └── models.py # Behavior models
└── observability/
└── tracing.py # OpenTelemetry setup
```

## Ключевые файлы
## API Endpoints

- `app/main.py` — точка входа, lifespan context manager
- `app/api.py` — все API endpoints
- `app/core/config.py` — Settings с SettingsConfigDict
- `app/core/llm_client.py` — OpenRouterClient
- `app/services/chat_service.py` — бизнес-логика чатов
- `GET /` — status
- `GET /health` — health check
- `POST /api/v1/chat` — standalone chat
- `POST /api/v1/projects` — create project
- `GET /api/v1/projects` — list projects
- `POST /api/v1/projects/{id}/chat` — chat in project
- `GET /api/v1/projects/{id}/history` — chat history
- `GET /api/v1/behavior/schema` — current behavior

## Стек

Expand All @@ -61,15 +74,20 @@ app/
- OpenTelemetry (traces, logs, metrics)
- OpenRouter (LLM)
- uv (package manager)
- structlog (logging)
- httpx (HTTP client)
- notion-client (Notion API)

## Конвенции

- Pydantic v2: `model_config = SettingsConfigDict(...)` вместо `class Config`
- Datetime: `datetime.now(UTC)` вместо `datetime.utcnow()`
- FastAPI: lifespan context manager вместо `@app.on_event`
- Тесты: `OTEL_SDK_DISABLED=true` для отключения трейсинга
- Логирование: `enrich_context(event="name").info("message")`

## Связанные репозитории

- `app-crewai-cluster` — CrewAI агенты (отдельный сервис)
- `app-release` — Helm charts и GitOps
- `app-poly-gitops-k8s` — GitOps манифесты (ArgoCD Applications)
- `app-poly-gitops-helm` — Helm chart для сервисов
- `app-poly-gitops-crewai` — CrewAI мониторинг
43 changes: 12 additions & 31 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,56 +1,37 @@
# =============================================================================
# Chat API Configuration
# Chat API Configuration (synced with Helm values)
# =============================================================================

# -----------------------------------------------------------------------------
# LLM Provider (OpenRouter)
# OpenRouter LLM
# -----------------------------------------------------------------------------
OPENROUTER_API_KEY=sk-or-v1-...
OPENROUTER_MODEL=anthropic/claude-opus-4
OPENROUTER_API_URL=https://openrouter.ai/api/v1/chat/completions
OPENROUTER_HTTP_REFERER=https://your-app.com
OPENROUTER_X_TITLE=Chat API

# Legacy (if using LiteLLM proxy or OpenAI directly)
OPENAI_API_KEY=
LLM_API_URL=http://localhost:4000
CHAT_MODEL=openai/gpt-4.1
OPENROUTER_MODEL=anthropic/claude-sonnet-4

# -----------------------------------------------------------------------------
# Database
# -----------------------------------------------------------------------------
# SQLite (default for local dev)
DATABASE_URL=sqlite:///db.sqlite3

# PostgreSQL (docker-compose)
POSTGRES_USER=chatuser
POSTGRES_PASSWORD=changeme
POSTGRES_DB=chatdb
# DATABASE_URL=postgresql://chatuser:changeme@localhost:5432/chatdb

# -----------------------------------------------------------------------------
# Notion Integration (optional)
# -----------------------------------------------------------------------------
NOTION_TOKEN=
NOTION_PAGE_ID=
# PostgreSQL (production via PGO secret)
# DATABASE_URL=postgresql://user:pass@host:5432/chatdb

# -----------------------------------------------------------------------------
# OpenTelemetry (observability)
# OpenTelemetry
# -----------------------------------------------------------------------------
OTEL_SERVICE_NAME=chat-api
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
ENVIRONMENT=development

# Kubernetes metadata (auto-populated in K8s)
K8S_POD_NAME=
K8S_NAMESPACE=
K8S_NODE_NAME=
K8S_DEPLOYMENT_NAME=
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_TRACES_EXPORTER=otlp
OTEL_LOGS_EXPORTER=otlp

# Disable OTEL for local dev/testing
# Disable for local dev/testing
# OTEL_SDK_DISABLED=true

# -----------------------------------------------------------------------------
# Application
# -----------------------------------------------------------------------------
PROJECT_NAME=ChatMicroservice
ENVIRONMENT=development
LOG_LEVEL=INFO
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ build/
.coverage
htmlcov/
*.sqlite3
node_modules/
65 changes: 61 additions & 4 deletions .releaserc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,77 @@
[
"@semantic-release/commit-analyzer",
{
"preset": "conventionalcommits",
"releaseRules": [
{"type": "feat", "release": "minor"},
{"type": "fix", "release": "patch"},
{"type": "perf", "release": "patch"},
{"type": "revert", "release": "patch"},
{"type": "docs", "release": "patch"},
{"type": "style", "release": "patch"},
{"type": "refactor", "release": "patch"},
{"type": "test", "release": "patch"},
{"type": "ci", "release": "patch"},
{"type": "chore", "release": "patch"},
{"breaking": true, "release": "major"}
]
{"type": "build", "release": "patch"},
{"breaking": true, "release": "major"},
{"scope": "no-release", "release": false}
],
"parserOpts": {
"noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES", "BREAKING"]
}
}
],
"@semantic-release/release-notes-generator",
"@semantic-release/github"
[
"@semantic-release/release-notes-generator",
{
"preset": "conventionalcommits",
"presetConfig": {
"types": [
{"type": "feat", "section": "🚀 Features"},
{"type": "fix", "section": "🐛 Bug Fixes"},
{"type": "perf", "section": "⚡ Performance Improvements"},
{"type": "revert", "section": "⏪ Reverts"},
{"type": "docs", "section": "📚 Documentation"},
{"type": "style", "section": "💄 Styles"},
{"type": "refactor", "section": "♻️ Code Refactoring"},
{"type": "test", "section": "✅ Tests"},
{"type": "ci", "section": "🔧 CI/CD"},
{"type": "chore", "section": "🏗️ Chores"},
{"type": "build", "section": "📦 Build System"}
]
},
"writerOpts": {
"commitsSort": ["subject", "scope"]
}
}
],
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md"
}
],
[
"@semantic-release/github",
{
"assets": [
{
"path": "CHANGELOG.md",
"label": "Changelog"
}
],
"successComment": false,
"failComment": false,
"releasedLabels": false
}
],
[
"@semantic-release/git",
{
"assets": ["CHANGELOG.md"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
}
22 changes: 12 additions & 10 deletions app/core/config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import os
from functools import lru_cache

from pydantic_settings import BaseSettings, SettingsConfigDict
Expand All @@ -8,16 +7,19 @@

class Settings(BaseSettings):
"""
Конфиг всего приложения.
Все переменные тянутся из .env — легко расширять и объяснять новым людям.
Конфиг приложения. Все переменные из ENV (синхронизировано с Helm values).
"""
openai_api_key: str = os.getenv("OPENAI_API_KEY", "")
llm_api_url: str = "http://localhost:4000"
chat_model: str = "openai/gpt-4.1"
project_name: str = "ChatMicroservice"
notion_token: str = os.getenv("NOTION_TOKEN", "")
notion_page_id: str = os.getenv("NOTION_PAGE_ID", "")
database_url: str = os.getenv("DATABASE_URL", "sqlite:///db.sqlite3")
# OpenRouter LLM
openrouter_api_key: str = ""
openrouter_api_url: str = "https://openrouter.ai/api/v1/chat/completions"
openrouter_model: str = "anthropic/claude-sonnet-4"

# Database
database_url: str = "sqlite:///db.sqlite3"

# Application
environment: str = "development"
log_level: str = "INFO"

model_config = SettingsConfigDict(
env_file=".env",
Expand Down
24 changes: 0 additions & 24 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,15 @@
from fastapi.middleware.cors import CORSMiddleware

from .api import api_router
from .core.config import get_settings
from .core.db import init_db
from .core.health import health_router
from .integrations.behavior_manager import BehaviorManager
from .integrations.notion_client import NotionClient
from .logger import enrich_context
from .observability.tracing import setup_tracing


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
"""Lifespan event handler for startup and shutdown."""
settings = get_settings()

# Startup: init database
enrich_context(event="startup_init_db_start").info("Starting database initialization")
try:
Expand All @@ -28,25 +23,6 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
enrich_context(event="startup_init_db_error", error=str(e)).error("Database initialization failed")
raise

# Startup: load behavior from Notion (if configured)
if settings.notion_token and settings.notion_page_id:
enrich_context(event="notion_config_found").info("Notion configuration found")
notion_client = NotionClient(settings.notion_token)
behavior_manager = BehaviorManager(notion_client, settings.notion_page_id)
app.state.behavior_manager = behavior_manager

enrich_context(event="startup_behavior_start").info("Starting behavior loading")
try:
await behavior_manager.refresh()
enrich_context(event="startup_behavior_success").info("Behavior loading completed")
except Exception as e:
enrich_context(event="startup_behavior_error", error=str(e)).error("Behavior loading failed")
# Не падаем на этой ошибке
else:
enrich_context(event="notion_config_missing").info(
"Notion configuration not found, skipping behavior loading"
)

enrich_context(event="startup").info("Application initialized")

yield # Application runs here
Expand Down
6 changes: 3 additions & 3 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
def test_settings_defaults():
"""Test Settings has correct defaults."""
settings = Settings()
assert settings.llm_api_url == "http://localhost:4000"
assert settings.chat_model == "openai/gpt-4.1"
assert settings.project_name == "ChatMicroservice"
assert settings.openrouter_api_url == "https://openrouter.ai/api/v1/chat/completions"
assert settings.openrouter_model == "anthropic/claude-sonnet-4"
assert settings.environment == "development"


def test_get_settings_cached():
Expand Down