Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,6 @@ robolearn-interface/
.env
.env.local

cookies.txt
cookies.txt

rag-agent/
4 changes: 4 additions & 0 deletions packages/api/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ dependencies = [
"uvicorn[standard]>=0.32.0",
"asyncpg>=0.30.0",
"sqlalchemy[asyncio]>=2.0.0",
"python-dotenv>=1.0.0",
# ChatKit + OpenAI Agents SDK for chat server
"openai-agents>=0.0.9",
"openai-chatkit>=1.4.0",
]

[project.optional-dependencies]
Expand Down
16 changes: 16 additions & 0 deletions packages/api/src/taskflow_api/chatkit_store/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
TaskFlow ChatKit Store Implementation.

Provides PostgreSQL-based Store for ChatKit conversation persistence.
Adapted from rag-agent/chatkit_store for TaskFlow API.
"""

from .config import StoreConfig
from .context import RequestContext
from .postgres_store import PostgresStore

__all__ = [
"StoreConfig",
"PostgresStore",
"RequestContext",
]
83 changes: 83 additions & 0 deletions packages/api/src/taskflow_api/chatkit_store/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""Configuration for TaskFlow ChatKit store."""

from pydantic import Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict


class StoreConfig(BaseSettings):
"""
PostgreSQL Store configuration for TaskFlow ChatKit.

All settings can be overridden via environment variables with the
TASKFLOW_CHATKIT_ prefix (e.g., TASKFLOW_CHATKIT_DATABASE_URL).
"""

model_config = SettingsConfigDict(
env_prefix="TASKFLOW_CHATKIT_",
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=False,
extra="ignore",
)

# Database connection
database_url: str = Field(
...,
description="PostgreSQL connection URL (postgresql+asyncpg://user:pass@host:port/db)",
)

# Connection pool settings
pool_size: int = Field(
default=20,
description="Maximum number of connections in the pool",
ge=1,
le=100,
)

max_overflow: int = Field(
default=10,
description="Maximum overflow connections beyond pool_size",
ge=0,
le=50,
)

pool_timeout: float = Field(
default=30.0,
description="Seconds to wait before timing out on getting a connection",
gt=0,
)

pool_recycle: int = Field(
default=3600, description="Seconds after which connections are recycled", ge=300
)

# Query settings
statement_timeout: int = Field(
default=30000, description="Statement timeout in milliseconds", ge=1000
)

# Schema - use taskflow_chat to isolate from main taskflow schema
schema_name: str = Field(default="taskflow_chat", description="Database schema name for tables")

@field_validator("database_url")
@classmethod
def validate_database_url(cls, v: str) -> str:
"""Ensure database URL uses asyncpg driver and fix SSL parameters."""
if not v.startswith("postgresql://") and not v.startswith("postgresql+asyncpg://"):
raise ValueError("database_url must start with postgresql:// or postgresql+asyncpg://")

# Convert to asyncpg if needed
if v.startswith("postgresql://"):
v = v.replace("postgresql://", "postgresql+asyncpg://", 1)

# Fix SSL parameters for asyncpg
if "sslmode=require" in v:
v = v.replace("sslmode=require", "ssl=require")
elif "sslmode=prefer" in v:
v = v.replace("sslmode=prefer", "ssl=prefer")
elif "sslmode=allow" in v:
v = v.replace("sslmode=allow", "ssl=allow")
elif "sslmode=disable" in v:
v = v.replace("sslmode=disable", "ssl=disable")

return v
37 changes: 37 additions & 0 deletions packages/api/src/taskflow_api/chatkit_store/context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Request context for ChatKit store operations."""

from typing import Any

from pydantic import BaseModel, Field


class RequestContext(BaseModel):
"""
Request context passed to all store operations.

Provides user isolation and optional metadata for access control,
logging, and tracing across all store operations.
"""

user_id: str | None = Field(
...,
description="Unique identifier for the user making the request",
min_length=1,
)

organization_id: str | None = Field(
default=None, description="Optional organization ID for multi-org tenancy"
)

request_id: str | None = Field(
default=None, description="Optional request ID for tracing and logging"
)

metadata: dict[str, Any] = Field(
default_factory=dict, description="Additional context metadata"
)

class Config:
"""Pydantic configuration."""

frozen = False # Allow mutation for adding trace info
Loading
Loading