850 lines
19 KiB
Markdown
850 lines
19 KiB
Markdown
|
|
# 💻 Development Guide
|
||
|
|
|
||
|
|
This guide provides comprehensive information for developers working on the Agentic RAG system, including setup, code structure, development workflows, and best practices.
|
||
|
|
|
||
|
|
## Development Environment Setup
|
||
|
|
|
||
|
|
### Prerequisites
|
||
|
|
|
||
|
|
- **Python 3.12+** - [Download Python](https://www.python.org/downloads/)
|
||
|
|
- **Node.js 18+** - [Download Node.js](https://nodejs.org/)
|
||
|
|
- **uv** - Python package manager ([Install uv](https://github.com/astral-sh/uv))
|
||
|
|
- **Git** - Version control
|
||
|
|
- **VS Code** (recommended) - [Download VS Code](https://code.visualstudio.com/)
|
||
|
|
|
||
|
|
### Initial Setup
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Clone the repository
|
||
|
|
git clone <repository-url>
|
||
|
|
cd agentic-rag-4
|
||
|
|
|
||
|
|
# Install Python dependencies
|
||
|
|
uv sync --dev
|
||
|
|
|
||
|
|
# Install frontend dependencies
|
||
|
|
cd web && npm install
|
||
|
|
|
||
|
|
# Copy configuration template
|
||
|
|
cp config.yaml config.local.yaml
|
||
|
|
|
||
|
|
# Set up environment variables
|
||
|
|
export OPENAI_API_KEY="your-key"
|
||
|
|
export RETRIEVAL_API_KEY="your-key"
|
||
|
|
```
|
||
|
|
|
||
|
|
### VS Code Configuration
|
||
|
|
|
||
|
|
Recommended VS Code extensions:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"recommendations": [
|
||
|
|
"ms-python.python",
|
||
|
|
"ms-python.black-formatter",
|
||
|
|
"charliermarsh.ruff",
|
||
|
|
"ms-python.mypy-type-checker",
|
||
|
|
"bradlc.vscode-tailwindcss",
|
||
|
|
"ms-vscode.vscode-typescript-next",
|
||
|
|
"esbenp.prettier-vscode"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Create `.vscode/settings.json`:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"python.defaultInterpreterPath": "./.venv/bin/python",
|
||
|
|
"python.linting.enabled": true,
|
||
|
|
"python.linting.ruffEnabled": true,
|
||
|
|
"python.formatting.provider": "black",
|
||
|
|
"python.testing.pytestEnabled": true,
|
||
|
|
"python.testing.pytestArgs": ["tests/"],
|
||
|
|
"editor.formatOnSave": true,
|
||
|
|
"editor.codeActionsOnSave": {
|
||
|
|
"source.organizeImports": true
|
||
|
|
},
|
||
|
|
"files.exclude": {
|
||
|
|
"**/__pycache__": true,
|
||
|
|
"**/.pytest_cache": true,
|
||
|
|
"**/.mypy_cache": true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Architecture Deep Dive
|
||
|
|
|
||
|
|
### Backend Architecture (FastAPI + LangGraph)
|
||
|
|
|
||
|
|
```
|
||
|
|
service/
|
||
|
|
├── main.py # FastAPI application entry point
|
||
|
|
├── config.py # Configuration management
|
||
|
|
├── ai_sdk_adapter.py # Data Stream Protocol adapter
|
||
|
|
├── ai_sdk_chat.py # AI SDK compatible endpoints
|
||
|
|
├── llm_client.py # LLM provider abstractions
|
||
|
|
├── sse.py # Server-Sent Events utilities
|
||
|
|
├── graph/ # LangGraph workflow
|
||
|
|
│ ├── graph.py # Agent workflow definition
|
||
|
|
│ ├── state.py # State management (TurnState, AgentState)
|
||
|
|
│ └── message_trimmer.py # Context window management
|
||
|
|
├── memory/ # Session persistence
|
||
|
|
│ ├── postgresql_memory.py # PostgreSQL checkpointer
|
||
|
|
│ └── store.py # Memory abstractions
|
||
|
|
├── retrieval/ # Information retrieval
|
||
|
|
│ └── agentic_retrieval.py # Tool implementations
|
||
|
|
├── schemas/ # Data models
|
||
|
|
│ └── messages.py # Pydantic models
|
||
|
|
└── utils/ # Shared utilities
|
||
|
|
├── logging.py # Structured logging
|
||
|
|
└── templates.py # Prompt templates
|
||
|
|
```
|
||
|
|
|
||
|
|
### Frontend Architecture (Next.js + assistant-ui)
|
||
|
|
|
||
|
|
```
|
||
|
|
web/src/
|
||
|
|
├── app/
|
||
|
|
│ ├── layout.tsx # Root layout with providers
|
||
|
|
│ ├── page.tsx # Main chat interface
|
||
|
|
│ ├── globals.css # Global styles + assistant-ui
|
||
|
|
│ └── api/ # Server-side API routes
|
||
|
|
│ ├── chat/route.ts # Chat proxy endpoint
|
||
|
|
│ └── langgraph/ # LangGraph API proxy
|
||
|
|
├── components/ # Reusable components
|
||
|
|
├── hooks/ # Custom React hooks
|
||
|
|
└── lib/ # Utility libraries
|
||
|
|
```
|
||
|
|
|
||
|
|
## Development Workflow
|
||
|
|
|
||
|
|
### 1. Start Development Services
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Terminal 1: Start backend in development mode
|
||
|
|
make dev-backend
|
||
|
|
# or
|
||
|
|
./scripts/start_service.sh --dev
|
||
|
|
|
||
|
|
# Terminal 2: Start frontend development server
|
||
|
|
make dev-web
|
||
|
|
# or
|
||
|
|
cd web && npm run dev
|
||
|
|
|
||
|
|
# Alternative: Start both simultaneously
|
||
|
|
make dev
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Development URLs
|
||
|
|
|
||
|
|
- **Backend API**: http://localhost:8000
|
||
|
|
- **API Documentation**: http://localhost:8000/docs
|
||
|
|
- **Frontend**: http://localhost:3000
|
||
|
|
- **Health Check**: http://localhost:8000/health
|
||
|
|
|
||
|
|
### 3. Hot Reloading
|
||
|
|
|
||
|
|
Both backend and frontend support hot reloading:
|
||
|
|
|
||
|
|
- **Backend**: uvicorn auto-reloads on Python file changes
|
||
|
|
- **Frontend**: Next.js hot-reloads on TypeScript/CSS changes
|
||
|
|
|
||
|
|
## Code Style and Standards
|
||
|
|
|
||
|
|
### Python Code Style
|
||
|
|
|
||
|
|
We use the following tools for Python code quality:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Format code with Black
|
||
|
|
uv run black service/ tests/
|
||
|
|
|
||
|
|
# Lint with Ruff
|
||
|
|
uv run ruff check service/ tests/
|
||
|
|
|
||
|
|
# Type checking with MyPy
|
||
|
|
uv run mypy service/
|
||
|
|
|
||
|
|
# Run all quality checks
|
||
|
|
make lint
|
||
|
|
```
|
||
|
|
|
||
|
|
### Python Coding Standards
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Example: Proper function documentation
|
||
|
|
async def stream_chat_response(request: ChatRequest) -> AsyncGenerator[str, None]:
|
||
|
|
"""
|
||
|
|
Stream chat response using agent workflow with PostgreSQL session memory.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
request: Chat request containing messages and session_id
|
||
|
|
|
||
|
|
Yields:
|
||
|
|
str: SSE formatted events for streaming response
|
||
|
|
|
||
|
|
Raises:
|
||
|
|
HTTPException: If workflow execution fails
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
# Implementation...
|
||
|
|
pass
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"Stream chat error: {e}", exc_info=True)
|
||
|
|
raise
|
||
|
|
```
|
||
|
|
|
||
|
|
### TypeScript/React Standards
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Example: Proper component structure
|
||
|
|
interface ChatInterfaceProps {
|
||
|
|
sessionId?: string;
|
||
|
|
initialMessages?: Message[];
|
||
|
|
}
|
||
|
|
|
||
|
|
export function ChatInterface({
|
||
|
|
sessionId,
|
||
|
|
initialMessages = []
|
||
|
|
}: ChatInterfaceProps) {
|
||
|
|
// Component implementation...
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Configuration Management
|
||
|
|
|
||
|
|
Use environment-based configuration:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# config.py example
|
||
|
|
from pydantic_settings import BaseSettings
|
||
|
|
from typing import Optional
|
||
|
|
|
||
|
|
class Config(BaseSettings):
|
||
|
|
provider: str = "openai"
|
||
|
|
openai_api_key: Optional[str] = None
|
||
|
|
retrieval_endpoint: str
|
||
|
|
|
||
|
|
class Config:
|
||
|
|
env_file = ".env"
|
||
|
|
env_prefix = "AGENTIC_"
|
||
|
|
```
|
||
|
|
|
||
|
|
## Testing Strategy
|
||
|
|
|
||
|
|
### Running Tests
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Run all tests
|
||
|
|
make test
|
||
|
|
|
||
|
|
# Run specific test types
|
||
|
|
make test-unit # Unit tests only
|
||
|
|
make test-integration # Integration tests only
|
||
|
|
make test-e2e # End-to-end tests
|
||
|
|
|
||
|
|
# Run with coverage
|
||
|
|
uv run pytest --cov=service --cov-report=html tests/
|
||
|
|
|
||
|
|
# Run specific test file
|
||
|
|
uv run pytest tests/unit/test_retrieval.py -v
|
||
|
|
|
||
|
|
# Run tests with debugging
|
||
|
|
uv run pytest -s -vvv tests/integration/test_api.py::test_chat_endpoint
|
||
|
|
```
|
||
|
|
|
||
|
|
### Test Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
tests/
|
||
|
|
├── unit/ # Unit tests (fast, isolated)
|
||
|
|
│ ├── test_config.py
|
||
|
|
│ ├── test_retrieval.py
|
||
|
|
│ ├── test_memory.py
|
||
|
|
│ └── test_graph.py
|
||
|
|
├── integration/ # Integration tests (with dependencies)
|
||
|
|
│ ├── test_api.py
|
||
|
|
│ ├── test_streaming.py
|
||
|
|
│ ├── test_full_workflow.py
|
||
|
|
│ └── test_e2e_tool_ui.py
|
||
|
|
└── conftest.py # Shared test fixtures
|
||
|
|
```
|
||
|
|
|
||
|
|
### Writing Tests
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Example unit test
|
||
|
|
import pytest
|
||
|
|
from service.retrieval.agentic_retrieval import RetrievalTool
|
||
|
|
|
||
|
|
class TestRetrievalTool:
|
||
|
|
@pytest.fixture
|
||
|
|
def tool(self):
|
||
|
|
return RetrievalTool(
|
||
|
|
endpoint="http://test-endpoint",
|
||
|
|
api_key="test-key"
|
||
|
|
)
|
||
|
|
|
||
|
|
async def test_search_standards(self, tool, httpx_mock):
|
||
|
|
# Mock HTTP response
|
||
|
|
httpx_mock.add_response(
|
||
|
|
url="http://test-endpoint/search",
|
||
|
|
json={"results": [{"title": "Test Standard"}]}
|
||
|
|
)
|
||
|
|
|
||
|
|
# Test the tool
|
||
|
|
result = await tool.search_standards("test query")
|
||
|
|
|
||
|
|
# Assertions
|
||
|
|
assert len(result["results"]) == 1
|
||
|
|
assert result["results"][0]["title"] == "Test Standard"
|
||
|
|
|
||
|
|
# Example integration test
|
||
|
|
class TestChatAPI:
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_streaming_response(self, client):
|
||
|
|
request_data = {
|
||
|
|
"messages": [{"role": "user", "content": "test question"}],
|
||
|
|
"session_id": "test_session"
|
||
|
|
}
|
||
|
|
|
||
|
|
response = client.post("/api/chat", json=request_data)
|
||
|
|
|
||
|
|
assert response.status_code == 200
|
||
|
|
assert response.headers["content-type"] == "text/event-stream"
|
||
|
|
```
|
||
|
|
|
||
|
|
## API Development
|
||
|
|
|
||
|
|
### Adding New Endpoints
|
||
|
|
|
||
|
|
1. **Define the schema** in `service/schemas/`:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# schemas/new_feature.py
|
||
|
|
from pydantic import BaseModel
|
||
|
|
from typing import List, Optional
|
||
|
|
|
||
|
|
class NewFeatureRequest(BaseModel):
|
||
|
|
query: str
|
||
|
|
options: Optional[List[str]] = []
|
||
|
|
|
||
|
|
class NewFeatureResponse(BaseModel):
|
||
|
|
result: str
|
||
|
|
metadata: dict
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Implement the logic** in appropriate module:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# service/new_feature.py
|
||
|
|
async def process_new_feature(request: NewFeatureRequest) -> NewFeatureResponse:
|
||
|
|
# Implementation
|
||
|
|
return NewFeatureResponse(
|
||
|
|
result="processed",
|
||
|
|
metadata={"took_ms": 100}
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Add the endpoint** in `service/main.py`:
|
||
|
|
|
||
|
|
```python
|
||
|
|
@app.post("/api/new-feature")
|
||
|
|
async def new_feature_endpoint(request: NewFeatureRequest):
|
||
|
|
try:
|
||
|
|
result = await process_new_feature(request)
|
||
|
|
return result
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"New feature error: {e}")
|
||
|
|
raise HTTPException(status_code=500, detail=str(e))
|
||
|
|
```
|
||
|
|
|
||
|
|
4. **Add tests**:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# tests/unit/test_new_feature.py
|
||
|
|
def test_new_feature_endpoint(client):
|
||
|
|
response = client.post("/api/new-feature", json={
|
||
|
|
"query": "test",
|
||
|
|
"options": ["option1"]
|
||
|
|
})
|
||
|
|
assert response.status_code == 200
|
||
|
|
```
|
||
|
|
|
||
|
|
### LangGraph Agent Development
|
||
|
|
|
||
|
|
#### Adding New Tools
|
||
|
|
|
||
|
|
1. **Define the tool** in `service/retrieval/`:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# agentic_retrieval.py
|
||
|
|
@tool
|
||
|
|
def new_search_tool(query: str, filters: Optional[dict] = None) -> dict:
|
||
|
|
"""
|
||
|
|
New search tool for specific domain.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
query: Search query string
|
||
|
|
filters: Optional search filters
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Search results with metadata
|
||
|
|
"""
|
||
|
|
# Implementation
|
||
|
|
return {"results": [], "metadata": {}}
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Register the tool** in `service/graph/graph.py`:
|
||
|
|
|
||
|
|
```python
|
||
|
|
def build_graph() -> CompiledGraph:
|
||
|
|
# Add the new tool to tools list
|
||
|
|
tools = [
|
||
|
|
retrieve_standard_regulation,
|
||
|
|
retrieve_doc_chunk_standard_regulation,
|
||
|
|
new_search_tool # Add new tool
|
||
|
|
]
|
||
|
|
|
||
|
|
# Rest of graph building...
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Update the system prompt** to include the new tool:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# config.yaml
|
||
|
|
llm:
|
||
|
|
rag:
|
||
|
|
agent_system_prompt: |
|
||
|
|
You have access to the following tools:
|
||
|
|
- retrieve_standard_regulation: Search standards/regulations
|
||
|
|
- retrieve_doc_chunk_standard_regulation: Search document chunks
|
||
|
|
- new_search_tool: Search specific domain
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Modifying Agent Workflow
|
||
|
|
|
||
|
|
The agent workflow is defined in `service/graph/graph.py`:
|
||
|
|
|
||
|
|
```python
|
||
|
|
def agent_node(state: TurnState, config: RunnableConfig) -> TurnState:
|
||
|
|
"""Main agent decision-making node"""
|
||
|
|
|
||
|
|
# Get conversation history
|
||
|
|
messages = state.get("messages", [])
|
||
|
|
|
||
|
|
# Call LLM with tools
|
||
|
|
response = llm_with_tools.invoke(messages, config)
|
||
|
|
|
||
|
|
# Update state
|
||
|
|
new_messages = messages + [response]
|
||
|
|
return {"messages": new_messages}
|
||
|
|
|
||
|
|
def should_continue(state: TurnState) -> str:
|
||
|
|
"""Decide whether to continue or finish"""
|
||
|
|
|
||
|
|
last_message = state["messages"][-1]
|
||
|
|
|
||
|
|
# If LLM called tools, continue to tools
|
||
|
|
if last_message.tool_calls:
|
||
|
|
return "tools"
|
||
|
|
|
||
|
|
# Otherwise, finish
|
||
|
|
return "post_process"
|
||
|
|
```
|
||
|
|
|
||
|
|
## Frontend Development
|
||
|
|
|
||
|
|
### assistant-ui Integration
|
||
|
|
|
||
|
|
The frontend uses `@assistant-ui/react` for the chat interface:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// app/page.tsx
|
||
|
|
import { Thread } from "@assistant-ui/react";
|
||
|
|
import { makeDataStreamRuntime } from "@assistant-ui/react-data-stream";
|
||
|
|
|
||
|
|
export default function ChatPage() {
|
||
|
|
const runtime = makeDataStreamRuntime({
|
||
|
|
api: "/api/chat",
|
||
|
|
});
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="h-screen">
|
||
|
|
<Thread runtime={runtime} />
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Adding Custom Tool UI
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// components/ToolUI.tsx
|
||
|
|
import { ToolCall, ToolCallContent } from "@assistant-ui/react";
|
||
|
|
|
||
|
|
export function CustomToolUI() {
|
||
|
|
return (
|
||
|
|
<ToolCall toolName="retrieve_standard_regulation">
|
||
|
|
<ToolCallContent>
|
||
|
|
{({ result }) => (
|
||
|
|
<div className="border rounded p-4">
|
||
|
|
<h3>Search Results</h3>
|
||
|
|
{result?.results?.map((item, index) => (
|
||
|
|
<div key={index} className="mt-2">
|
||
|
|
<strong>{item.title}</strong>
|
||
|
|
<p>{item.description}</p>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</ToolCallContent>
|
||
|
|
</ToolCall>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Styling with Tailwind CSS
|
||
|
|
|
||
|
|
The project uses Tailwind CSS with assistant-ui plugin:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// tailwind.config.ts
|
||
|
|
import { assistant } from "@assistant-ui/react/tailwindcss";
|
||
|
|
|
||
|
|
export default {
|
||
|
|
content: [
|
||
|
|
"./src/**/*.{js,ts,jsx,tsx,mdx}",
|
||
|
|
],
|
||
|
|
theme: {
|
||
|
|
extend: {},
|
||
|
|
},
|
||
|
|
plugins: [
|
||
|
|
assistant, // assistant-ui plugin
|
||
|
|
],
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
## Database Development
|
||
|
|
|
||
|
|
### Working with PostgreSQL Memory
|
||
|
|
|
||
|
|
The system uses PostgreSQL for session persistence via LangGraph's checkpointer:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# memory/postgresql_memory.py
|
||
|
|
from langgraph.checkpoint.postgres import PostgresSaver
|
||
|
|
|
||
|
|
class PostgreSQLMemoryManager:
|
||
|
|
def __init__(self, connection_string: str):
|
||
|
|
self.connection_string = connection_string
|
||
|
|
self.checkpointer = None
|
||
|
|
|
||
|
|
def get_checkpointer(self):
|
||
|
|
if not self.checkpointer:
|
||
|
|
self.checkpointer = PostgresSaver.from_conn_string(
|
||
|
|
self.connection_string
|
||
|
|
)
|
||
|
|
# Setup tables
|
||
|
|
self.checkpointer.setup()
|
||
|
|
return self.checkpointer
|
||
|
|
```
|
||
|
|
|
||
|
|
### Database Migrations
|
||
|
|
|
||
|
|
For schema changes, update the PostgreSQL setup:
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- migrations/001_add_metadata.sql
|
||
|
|
ALTER TABLE checkpoints
|
||
|
|
ADD COLUMN metadata JSONB DEFAULT '{}';
|
||
|
|
|
||
|
|
CREATE INDEX idx_checkpoints_metadata
|
||
|
|
ON checkpoints USING GIN (metadata);
|
||
|
|
```
|
||
|
|
|
||
|
|
## Debugging
|
||
|
|
|
||
|
|
### Backend Debugging
|
||
|
|
|
||
|
|
1. **Enable debug logging**:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
export LOG_LEVEL=DEBUG
|
||
|
|
make dev-backend
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Use Python debugger**:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Add to code where you want to break
|
||
|
|
import pdb; pdb.set_trace()
|
||
|
|
|
||
|
|
# Or use breakpoint() in Python 3.7+
|
||
|
|
breakpoint()
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **VS Code debugging**:
|
||
|
|
|
||
|
|
Create `.vscode/launch.json`:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"version": "0.2.0",
|
||
|
|
"configurations": [
|
||
|
|
{
|
||
|
|
"name": "FastAPI Debug",
|
||
|
|
"type": "python",
|
||
|
|
"request": "launch",
|
||
|
|
"program": "${workspaceFolder}/.venv/bin/uvicorn",
|
||
|
|
"args": [
|
||
|
|
"service.main:app",
|
||
|
|
"--reload",
|
||
|
|
"--host", "127.0.0.1",
|
||
|
|
"--port", "8000"
|
||
|
|
],
|
||
|
|
"console": "integratedTerminal",
|
||
|
|
"env": {
|
||
|
|
"PYTHONPATH": "${workspaceFolder}",
|
||
|
|
"LOG_LEVEL": "DEBUG"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Frontend Debugging
|
||
|
|
|
||
|
|
1. **Browser DevTools**: Use React DevTools and Network tab
|
||
|
|
|
||
|
|
2. **Next.js debugging**:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Start with debug mode
|
||
|
|
cd web && npm run dev -- --inspect
|
||
|
|
|
||
|
|
# Or use VS Code debugger
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Console logging**:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Add debug logs
|
||
|
|
console.log("Chat API request:", { messages, sessionId });
|
||
|
|
console.log("Backend response:", response);
|
||
|
|
```
|
||
|
|
|
||
|
|
## Performance Optimization
|
||
|
|
|
||
|
|
### Backend Performance
|
||
|
|
|
||
|
|
1. **Database connection pooling**:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# config.yaml
|
||
|
|
postgresql:
|
||
|
|
pool_size: 20
|
||
|
|
max_overflow: 10
|
||
|
|
pool_timeout: 30
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Async request handling**:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Use async/await properly
|
||
|
|
async def handle_request():
|
||
|
|
# Good: concurrent execution
|
||
|
|
results = await asyncio.gather(
|
||
|
|
tool1.search(query),
|
||
|
|
tool2.search(query)
|
||
|
|
)
|
||
|
|
|
||
|
|
# Avoid: sequential execution
|
||
|
|
# result1 = await tool1.search(query)
|
||
|
|
# result2 = await tool2.search(query)
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Memory management**:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Limit conversation history
|
||
|
|
def trim_conversation(messages: List[Message], max_tokens: int = 32000):
|
||
|
|
# Implementation to keep conversations under token limit
|
||
|
|
pass
|
||
|
|
```
|
||
|
|
|
||
|
|
### Frontend Performance
|
||
|
|
|
||
|
|
1. **Code splitting**:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Lazy load components
|
||
|
|
const HeavyComponent = lazy(() => import('./HeavyComponent'));
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Optimize bundle size**:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd web && npm run build
|
||
|
|
npm run analyze # If you have bundle analyzer
|
||
|
|
```
|
||
|
|
|
||
|
|
## Common Development Tasks
|
||
|
|
|
||
|
|
### Adding Configuration Options
|
||
|
|
|
||
|
|
1. **Update config schema**:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# config.py
|
||
|
|
class AppConfig(BaseSettings):
|
||
|
|
new_feature_enabled: bool = False
|
||
|
|
new_feature_timeout: int = 30
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Use in code**:
|
||
|
|
|
||
|
|
```python
|
||
|
|
config = get_config()
|
||
|
|
if config.app.new_feature_enabled:
|
||
|
|
# Feature implementation
|
||
|
|
pass
|
||
|
|
```
|
||
|
|
|
||
|
|
### Adding New Dependencies
|
||
|
|
|
||
|
|
1. **Python dependencies**:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Add to pyproject.toml
|
||
|
|
uv add fastapi-users[sqlalchemy]
|
||
|
|
|
||
|
|
# For development dependencies
|
||
|
|
uv add --dev pytest-xdist
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Frontend dependencies**:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd web
|
||
|
|
npm install @types/lodash
|
||
|
|
npm install --save-dev @testing-library/react
|
||
|
|
```
|
||
|
|
|
||
|
|
### Environment Management
|
||
|
|
|
||
|
|
Create environment-specific configs:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Development
|
||
|
|
cp config.yaml config.dev.yaml
|
||
|
|
|
||
|
|
# Production
|
||
|
|
cp config.yaml config.prod.yaml
|
||
|
|
|
||
|
|
# Use specific config
|
||
|
|
export CONFIG_FILE=config.dev.yaml
|
||
|
|
make dev-backend
|
||
|
|
```
|
||
|
|
|
||
|
|
## Troubleshooting Development Issues
|
||
|
|
|
||
|
|
### Common Issues
|
||
|
|
|
||
|
|
1. **Port conflicts**:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Check what's using port 8000
|
||
|
|
make port-check
|
||
|
|
|
||
|
|
# Kill processes on common ports
|
||
|
|
make port-kill
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Python import errors**:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Ensure PYTHONPATH is set
|
||
|
|
export PYTHONPATH="${PWD}:${PYTHONPATH}"
|
||
|
|
|
||
|
|
# Or use uv run
|
||
|
|
uv run python -m service.main
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Database connection issues**:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Test PostgreSQL connection
|
||
|
|
psql -h localhost -U user -d database -c "SELECT 1;"
|
||
|
|
|
||
|
|
# Check connection string format
|
||
|
|
echo $DATABASE_URL
|
||
|
|
```
|
||
|
|
|
||
|
|
4. **Frontend build errors**:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Clear Next.js cache
|
||
|
|
cd web && rm -rf .next
|
||
|
|
|
||
|
|
# Reinstall dependencies
|
||
|
|
rm -rf node_modules package-lock.json
|
||
|
|
npm install
|
||
|
|
```
|
||
|
|
|
||
|
|
### Development Best Practices
|
||
|
|
|
||
|
|
1. **Use feature branches**:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
git checkout -b feature/new-feature
|
||
|
|
# Make changes
|
||
|
|
git commit -m "Add new feature"
|
||
|
|
git push origin feature/new-feature
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Write tests first** (TDD approach):
|
||
|
|
|
||
|
|
```python
|
||
|
|
# Write test first
|
||
|
|
def test_new_feature():
|
||
|
|
assert new_feature("input") == "expected"
|
||
|
|
|
||
|
|
# Then implement
|
||
|
|
def new_feature(input: str) -> str:
|
||
|
|
return "expected"
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Keep commits small and focused**:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Good commit messages
|
||
|
|
git commit -m "Add PostgreSQL connection pooling"
|
||
|
|
git commit -m "Fix citation parsing edge case"
|
||
|
|
git commit -m "Update frontend dependencies"
|
||
|
|
```
|
||
|
|
|
||
|
|
4. **Document as you go**:
|
||
|
|
|
||
|
|
```python
|
||
|
|
def complex_function(param: str) -> dict:
|
||
|
|
"""
|
||
|
|
Brief description of what this function does.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
param: Description of parameter
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Description of return value
|
||
|
|
|
||
|
|
Example:
|
||
|
|
>>> result = complex_function("test")
|
||
|
|
>>> assert result["status"] == "success"
|
||
|
|
"""
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
This development guide provides the foundation for contributing to the Agentic RAG project. For specific questions or advanced topics, refer to the code comments and existing implementations as examples.
|