ya-agent-platform 0.58.3__py3-none-any.whl

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.
@@ -0,0 +1,4 @@
1
+ from ya_agent_platform.app import create_app
2
+ from ya_agent_platform.config import PlatformSettings, get_settings
3
+
4
+ __all__ = ["PlatformSettings", "create_app", "get_settings"]
@@ -0,0 +1,3 @@
1
+ from ya_agent_platform.cli import cli
2
+
3
+ cli()
@@ -0,0 +1,71 @@
1
+ from __future__ import annotations
2
+
3
+ from logging.config import fileConfig
4
+ from typing import Any
5
+
6
+ from alembic import context
7
+ from sqlalchemy import create_engine, pool
8
+ from ya_agent_platform.config import PlatformSettings
9
+ from ya_agent_platform.db import tables as _tables # noqa: F401
10
+ from ya_agent_platform.db.engine import to_sync_database_url
11
+ from ya_agent_platform.db.tables import Base
12
+
13
+ config = context.config
14
+ if config.config_file_name is not None:
15
+ fileConfig(config.config_file_name)
16
+
17
+ target_metadata = Base.metadata
18
+
19
+ settings = PlatformSettings()
20
+ if not settings.database_url:
21
+ msg = "YA_PLATFORM_DATABASE_URL is not set. Cannot run migrations."
22
+ raise RuntimeError(msg)
23
+
24
+
25
+ def get_url() -> str:
26
+ database_url = settings.database_url
27
+ if database_url is None:
28
+ msg = "database_url is None"
29
+ raise RuntimeError(msg)
30
+ return to_sync_database_url(database_url)
31
+
32
+
33
+ def include_object(obj: Any, name: str | None, type_: str, reflected: bool, compare_to: Any) -> bool:
34
+ return not (type_ == "table" and reflected and compare_to is None)
35
+
36
+
37
+ def run_migrations_offline() -> None:
38
+ context.configure(
39
+ url=get_url(),
40
+ target_metadata=target_metadata,
41
+ include_object=include_object,
42
+ literal_binds=True,
43
+ dialect_opts={"paramstyle": "named"},
44
+ compare_type=True,
45
+ compare_server_default=True,
46
+ )
47
+
48
+ with context.begin_transaction():
49
+ context.run_migrations()
50
+
51
+
52
+ def run_migrations_online() -> None:
53
+ connectable = create_engine(get_url(), poolclass=pool.NullPool)
54
+
55
+ with connectable.connect() as connection:
56
+ context.configure(
57
+ connection=connection,
58
+ target_metadata=target_metadata,
59
+ include_object=include_object,
60
+ compare_type=True,
61
+ compare_server_default=True,
62
+ )
63
+
64
+ with context.begin_transaction():
65
+ context.run_migrations()
66
+
67
+
68
+ if context.is_offline_mode():
69
+ run_migrations_offline()
70
+ else:
71
+ run_migrations_online()
@@ -0,0 +1,27 @@
1
+ """${message}
2
+
3
+ Revision ID: ${up_revision}
4
+ Revises: ${down_revision | comma,n}
5
+ Create Date: ${create_date}
6
+
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+
13
+ ${imports if imports else ""}
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision = ${repr(up_revision)}
17
+ down_revision = ${repr(down_revision)}
18
+ branch_labels = ${repr(branch_labels)}
19
+ depends_on = ${repr(depends_on)}
20
+
21
+
22
+ def upgrade() -> None:
23
+ ${upgrades if upgrades else "pass"}
24
+
25
+
26
+ def downgrade() -> None:
27
+ ${downgrades if downgrades else "pass"}
@@ -0,0 +1 @@
1
+ """Alembic migration versions for ya-agent-platform."""
@@ -0,0 +1,44 @@
1
+ # Alembic configuration file.
2
+ # Production use: `ya-agent-platform migrate` or `ya-agent-platform db upgrade`.
3
+
4
+ [alembic]
5
+ script_location = %(here)s/alembic
6
+ file_template = %%(year)d%%(month).2d%%(day).2d_%%(rev)s_%%(slug)s
7
+ prepend_sys_path = .
8
+ path_separator = os
9
+
10
+ # Database URL is read from YA_PLATFORM_DATABASE_URL in env.py.
11
+ # Do not set sqlalchemy.url here.
12
+
13
+ [loggers]
14
+ keys = root,sqlalchemy,alembic
15
+
16
+ [handlers]
17
+ keys = console
18
+
19
+ [formatters]
20
+ keys = generic
21
+
22
+ [logger_root]
23
+ level = WARNING
24
+ handlers = console
25
+
26
+ [logger_sqlalchemy]
27
+ level = WARNING
28
+ handlers =
29
+ qualname = sqlalchemy.engine
30
+
31
+ [logger_alembic]
32
+ level = INFO
33
+ handlers =
34
+ qualname = alembic
35
+
36
+ [handler_console]
37
+ class = StreamHandler
38
+ args = (sys.stderr,)
39
+ level = NOTSET
40
+ formatter = generic
41
+
42
+ [formatter_generic]
43
+ format = %(levelname)-5.5s [%(name)s] %(message)s
44
+ datefmt = %H:%M:%S
@@ -0,0 +1 @@
1
+ __all__ = []
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+
3
+ from fastapi import APIRouter, Request
4
+ from pydantic import BaseModel
5
+ from sqlalchemy import text
6
+
7
+ router = APIRouter(tags=["health"])
8
+
9
+
10
+ class HealthStatus(BaseModel):
11
+ status: str
12
+ postgres: str
13
+ redis: str
14
+
15
+
16
+ @router.get("/healthz", response_model=HealthStatus)
17
+ async def healthz(request: Request) -> HealthStatus:
18
+ postgres = "unavailable"
19
+ redis = "unavailable"
20
+
21
+ db_engine = getattr(request.app.state, "db_engine", None)
22
+ if db_engine is not None:
23
+ try:
24
+ async with db_engine.connect() as connection:
25
+ await connection.execute(text("SELECT 1"))
26
+ postgres = "ok"
27
+ except Exception:
28
+ postgres = "error"
29
+
30
+ redis_client = getattr(request.app.state, "redis", None)
31
+ if redis_client is not None:
32
+ try:
33
+ await redis_client.ping()
34
+ redis = "ok"
35
+ except Exception:
36
+ redis = "error"
37
+
38
+ status = "degraded" if "error" in {postgres, redis} else "ok"
39
+ return HealthStatus(status=status, postgres=postgres, redis=redis)
@@ -0,0 +1,61 @@
1
+ from __future__ import annotations
2
+
3
+ from fastapi import APIRouter
4
+ from pydantic import BaseModel
5
+
6
+ from ya_agent_platform.config import get_settings
7
+
8
+ router = APIRouter(prefix="/api/v1/platform", tags=["platform"])
9
+
10
+
11
+ class PlatformInfo(BaseModel):
12
+ name: str
13
+ environment: str
14
+ public_base_url: str
15
+ surfaces: list[str]
16
+ bridge_model: str
17
+ runtime_model: str
18
+
19
+
20
+ class PlatformTopology(BaseModel):
21
+ nodes: list[str]
22
+ edges: list[str]
23
+
24
+
25
+ @router.get("/info", response_model=PlatformInfo)
26
+ async def platform_info() -> PlatformInfo:
27
+ settings = get_settings()
28
+ return PlatformInfo(
29
+ name=settings.app_name,
30
+ environment=settings.environment,
31
+ public_base_url=settings.public_base_url,
32
+ surfaces=["management-api", "chat-api", "bridge-api", "chat-ui"],
33
+ bridge_model="bridge adapters connect external IM systems to normalized platform events",
34
+ runtime_model="agent sessions run through ya-agent-sdk based runtimes and workers",
35
+ )
36
+
37
+
38
+ @router.get("/topology", response_model=PlatformTopology)
39
+ async def platform_topology() -> PlatformTopology:
40
+ return PlatformTopology(
41
+ nodes=[
42
+ "chat-ui",
43
+ "management-api",
44
+ "chat-api",
45
+ "bridge-api",
46
+ "runtime-control",
47
+ "runtime-workers",
48
+ "postgres",
49
+ "redis-or-message-bus",
50
+ ],
51
+ edges=[
52
+ "chat-ui -> management-api",
53
+ "chat-ui -> chat-api",
54
+ "bridge adapters -> bridge-api",
55
+ "management-api -> runtime-control",
56
+ "chat-api -> runtime-control",
57
+ "runtime-control -> runtime-workers",
58
+ "runtime-control -> postgres",
59
+ "runtime-control -> redis-or-message-bus",
60
+ ],
61
+ )
@@ -0,0 +1,136 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import AsyncIterator
4
+ from contextlib import asynccontextmanager
5
+ from pathlib import Path
6
+
7
+ from fastapi import FastAPI, HTTPException
8
+ from fastapi.middleware.cors import CORSMiddleware
9
+ from fastapi.responses import FileResponse
10
+
11
+ from ya_agent_platform.api.health import router as health_router
12
+ from ya_agent_platform.api.platform import router as platform_router
13
+ from ya_agent_platform.config import PlatformSettings, get_settings
14
+ from ya_agent_platform.db.engine import create_engine
15
+ from ya_agent_platform.redis import create_redis_client
16
+
17
+ _RESERVED_FRONTEND_PATHS = ("api", "docs", "redoc", "openapi.json", "healthz")
18
+
19
+
20
+ @asynccontextmanager
21
+ async def _lifespan(app: FastAPI) -> AsyncIterator[None]:
22
+ settings = get_settings()
23
+
24
+ app.state.db_engine = None
25
+ app.state.redis = None
26
+
27
+ if settings.database_url:
28
+ app.state.db_engine = create_engine(
29
+ settings.database_url,
30
+ echo=settings.database_echo,
31
+ pool_size=settings.database_pool_size,
32
+ max_overflow=settings.database_max_overflow,
33
+ pool_recycle=settings.database_pool_recycle_seconds,
34
+ )
35
+
36
+ if settings.redis_url:
37
+ app.state.redis = create_redis_client(settings.redis_url)
38
+
39
+ try:
40
+ yield
41
+ finally:
42
+ redis_client = app.state.redis
43
+ if redis_client is not None:
44
+ await redis_client.aclose()
45
+
46
+ db_engine = app.state.db_engine
47
+ if db_engine is not None:
48
+ await db_engine.dispose()
49
+
50
+
51
+ def _resolve_frontend_target(web_dist_dir: Path, requested_path: str) -> Path:
52
+ relative_path = requested_path.strip("/")
53
+ if relative_path == "":
54
+ return web_dist_dir / "index.html"
55
+
56
+ candidate = (web_dist_dir / relative_path).resolve()
57
+ web_root = web_dist_dir.resolve()
58
+
59
+ try:
60
+ candidate.relative_to(web_root)
61
+ except ValueError as exc:
62
+ raise HTTPException(status_code=404, detail="Invalid frontend asset path.") from exc
63
+
64
+ if candidate.is_file():
65
+ return candidate
66
+
67
+ return web_dist_dir / "index.html"
68
+
69
+
70
+ def _register_frontend(app: FastAPI, settings: PlatformSettings) -> bool:
71
+ web_dist_dir = settings.web_dist_dir
72
+ if web_dist_dir is None:
73
+ return False
74
+
75
+ index_file = web_dist_dir / "index.html"
76
+ if not web_dist_dir.exists() or not index_file.exists():
77
+ return False
78
+
79
+ @app.get("/", include_in_schema=False)
80
+ async def frontend_index() -> FileResponse:
81
+ return FileResponse(index_file)
82
+
83
+ @app.get("/{full_path:path}", include_in_schema=False)
84
+ async def frontend_route(full_path: str) -> FileResponse:
85
+ normalized_path = full_path.strip("/")
86
+ if normalized_path in _RESERVED_FRONTEND_PATHS or any(
87
+ normalized_path.startswith(f"{prefix}/") for prefix in _RESERVED_FRONTEND_PATHS if "/" not in prefix
88
+ ):
89
+ raise HTTPException(status_code=404, detail="Route not found.")
90
+
91
+ return FileResponse(_resolve_frontend_target(web_dist_dir, normalized_path))
92
+
93
+ return True
94
+
95
+
96
+ def create_app() -> FastAPI:
97
+ settings = get_settings()
98
+
99
+ app = FastAPI(
100
+ title=settings.app_name,
101
+ version="0.1.0",
102
+ description="Cloud-ready agent platform backend built on top of ya-agent-sdk.",
103
+ lifespan=_lifespan,
104
+ )
105
+ app.state.settings = settings
106
+
107
+ app.add_middleware(
108
+ CORSMiddleware,
109
+ allow_origins=settings.allow_origins,
110
+ allow_credentials=True,
111
+ allow_methods=["*"],
112
+ allow_headers=["*"],
113
+ )
114
+
115
+ app.include_router(health_router)
116
+ app.include_router(platform_router)
117
+
118
+ frontend_registered = _register_frontend(app, settings)
119
+
120
+ if not frontend_registered:
121
+
122
+ @app.get("/")
123
+ async def index() -> dict[str, object]:
124
+ return {
125
+ "name": settings.app_name,
126
+ "environment": settings.environment,
127
+ "docs_url": "/docs",
128
+ "spec_path": "packages/ya-agent-platform/spec",
129
+ "surfaces": {
130
+ "admin": settings.admin_mount_path,
131
+ "chat": settings.chat_mount_path,
132
+ "bridges": settings.bridge_mount_path,
133
+ },
134
+ }
135
+
136
+ return app
@@ -0,0 +1,117 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ import click
6
+ import uvicorn
7
+
8
+ from ya_agent_platform.config import get_settings
9
+
10
+
11
+ @click.group()
12
+ def cli() -> None:
13
+ """YA Agent Platform management CLI."""
14
+
15
+
16
+ def _alembic_config():
17
+ """Build an Alembic Config from the package's alembic.ini."""
18
+ from alembic.config import Config
19
+
20
+ ini_path = Path(__file__).parent / "alembic.ini"
21
+ return Config(str(ini_path))
22
+
23
+
24
+ def _ensure_database_url() -> str:
25
+ settings = get_settings()
26
+ if settings.database_url:
27
+ return settings.database_url
28
+
29
+ click.echo("Error: YA_PLATFORM_DATABASE_URL is required.", err=True)
30
+ raise SystemExit(1)
31
+
32
+
33
+ def _apply_database_migrations(revision: str = "head") -> None:
34
+ from alembic import command
35
+
36
+ _ensure_database_url()
37
+ command.upgrade(_alembic_config(), revision)
38
+
39
+
40
+ @cli.command("serve")
41
+ @click.option("--host", default=None, help="Bind host for the HTTP server.")
42
+ @click.option("--port", default=None, type=int, help="Bind port for the HTTP server.")
43
+ @click.option("--reload/--no-reload", default=None, help="Enable or disable code reload.")
44
+ @click.option("--migrate/--no-migrate", default=None, help="Run database migrations before starting the server.")
45
+ def serve(host: str | None, port: int | None, reload: bool | None, migrate: bool | None) -> None:
46
+ settings = get_settings()
47
+ resolved_host = host or settings.host
48
+ resolved_port = port or settings.port
49
+ resolved_reload = settings.reload if reload is None else reload
50
+ resolved_migrate = settings.auto_migrate if migrate is None else migrate
51
+
52
+ if resolved_migrate and settings.database_url:
53
+ _apply_database_migrations()
54
+ click.echo("Database migrations applied.")
55
+
56
+ uvicorn.run(
57
+ "ya_agent_platform.app:create_app",
58
+ factory=True,
59
+ host=resolved_host,
60
+ port=resolved_port,
61
+ reload=resolved_reload,
62
+ )
63
+
64
+
65
+ @cli.command("migrate")
66
+ @click.option("--revision", default="head", help="Target revision for the migration run.")
67
+ def migrate_command(revision: str) -> None:
68
+ _apply_database_migrations(revision)
69
+ click.echo(f"Database upgraded to {revision}.")
70
+
71
+
72
+ @cli.group()
73
+ def db() -> None:
74
+ """Database migration and management commands."""
75
+
76
+
77
+ @db.command()
78
+ @click.option("--revision", default="head", help="Target revision (default: head).")
79
+ def upgrade(revision: str) -> None:
80
+ _apply_database_migrations(revision)
81
+ click.echo(f"Database upgraded to {revision}.")
82
+
83
+
84
+ @db.command()
85
+ @click.option("--revision", default="-1", help="Target revision (default: -1, one step back).")
86
+ def downgrade(revision: str) -> None:
87
+ from alembic import command
88
+
89
+ _ensure_database_url()
90
+ command.downgrade(_alembic_config(), revision)
91
+ click.echo(f"Database downgraded to {revision}.")
92
+
93
+
94
+ @db.command("migrate")
95
+ @click.argument("message")
96
+ def create_migration(message: str) -> None:
97
+ from alembic import command
98
+
99
+ _ensure_database_url()
100
+ command.revision(_alembic_config(), message=message, autogenerate=True)
101
+ click.echo(f"Migration generated: {message}")
102
+
103
+
104
+ @db.command()
105
+ def current() -> None:
106
+ from alembic import command
107
+
108
+ _ensure_database_url()
109
+ command.current(_alembic_config(), verbose=True)
110
+
111
+
112
+ @db.command()
113
+ def history() -> None:
114
+ from alembic import command
115
+
116
+ _ensure_database_url()
117
+ command.history(_alembic_config(), verbose=True)
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+
3
+ from functools import lru_cache
4
+ from pathlib import Path
5
+
6
+ from pydantic import Field
7
+ from pydantic_settings import BaseSettings, SettingsConfigDict
8
+
9
+
10
+ class PlatformSettings(BaseSettings):
11
+ model_config = SettingsConfigDict(
12
+ env_prefix="YA_PLATFORM_",
13
+ env_file=".env",
14
+ env_file_encoding="utf-8",
15
+ extra="ignore",
16
+ )
17
+
18
+ app_name: str = "YA Agent Platform"
19
+ environment: str = "development"
20
+ host: str = "127.0.0.1"
21
+ port: int = 9042
22
+ reload: bool = False
23
+ public_base_url: str = "http://127.0.0.1:9042"
24
+ admin_mount_path: str = "/admin"
25
+ chat_mount_path: str = "/chat"
26
+ bridge_mount_path: str = "/bridges"
27
+ web_dist_dir: Path | None = None
28
+ allow_origins: list[str] = Field(default_factory=lambda: ["http://127.0.0.1:5173", "http://localhost:5173"])
29
+
30
+ database_url: str | None = None
31
+ database_echo: bool = False
32
+ database_pool_size: int = 5
33
+ database_max_overflow: int = 10
34
+ database_pool_recycle_seconds: int = 3600
35
+
36
+ redis_url: str | None = None
37
+
38
+ auto_migrate: bool = True
39
+
40
+
41
+ @lru_cache(maxsize=1)
42
+ def get_settings() -> PlatformSettings:
43
+ return PlatformSettings()
@@ -0,0 +1,4 @@
1
+ from ya_agent_platform.db.base import Base
2
+ from ya_agent_platform.db.engine import create_engine, create_session_factory, to_sync_database_url
3
+
4
+ __all__ = ["Base", "create_engine", "create_session_factory", "to_sync_database_url"]
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from sqlalchemy.orm import DeclarativeBase
4
+
5
+
6
+ class Base(DeclarativeBase):
7
+ pass
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker, create_async_engine
4
+
5
+
6
+ def create_engine(database_url: str, **kwargs: object) -> AsyncEngine:
7
+ defaults: dict[str, object] = {
8
+ "echo": False,
9
+ "pool_size": 5,
10
+ "max_overflow": 10,
11
+ "pool_pre_ping": True,
12
+ "pool_recycle": 3600,
13
+ }
14
+ defaults.update(kwargs)
15
+ return create_async_engine(database_url, **defaults)
16
+
17
+
18
+ def create_session_factory(engine: AsyncEngine) -> async_sessionmaker[AsyncSession]:
19
+ return async_sessionmaker(engine, expire_on_commit=False)
20
+
21
+
22
+ def to_sync_database_url(database_url: str) -> str:
23
+ return database_url.replace("postgresql+asyncpg://", "postgresql+psycopg://").replace(
24
+ "postgresql+psycopg_async://", "postgresql+psycopg://"
25
+ )
@@ -0,0 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ from ya_agent_platform.db.base import Base
4
+
5
+ # Import ORM models here so Alembic autogenerate can discover them.
6
+ # Example:
7
+ # from ya_agent_platform.db.workspace import Workspace
8
+
9
+ __all__ = ["Base"]
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from redis.asyncio import Redis
4
+
5
+
6
+ def create_redis_client(redis_url: str, *, health_check_interval: int = 30) -> Redis:
7
+ return Redis.from_url(redis_url, health_check_interval=health_check_interval)
@@ -0,0 +1,163 @@
1
+ Metadata-Version: 2.4
2
+ Name: ya-agent-platform
3
+ Version: 0.58.3
4
+ Summary: Cloud-ready agent platform built on top of ya-agent-sdk
5
+ Project-URL: Repository, https://github.com/wh1isper/ya-mono
6
+ Author-email: wh1isper <jizhongsheng957@gmail.com>
7
+ Keywords: agent-platform,ai-agent,fastapi,python
8
+ Classifier: Framework :: FastAPI
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
+ Requires-Python: <3.14,>=3.11
17
+ Requires-Dist: alembic>=1.16.0
18
+ Requires-Dist: click>=8.0
19
+ Requires-Dist: fastapi>=0.116.0
20
+ Requires-Dist: psycopg[binary]>=3.2.0
21
+ Requires-Dist: pydantic-settings>=2.0.0
22
+ Requires-Dist: pydantic>=2.12.0
23
+ Requires-Dist: redis[hiredis]>=6.0.0
24
+ Requires-Dist: sqlalchemy>=2.0.0
25
+ Requires-Dist: sse-starlette>=3.0.0
26
+ Requires-Dist: uvicorn[standard]>=0.35.0
27
+ Requires-Dist: ya-agent-sdk[all]==0.58.3
28
+ Description-Content-Type: text/markdown
29
+
30
+ # YA Agent Platform
31
+
32
+ Cloud-ready agent platform package for the `ya-mono` workspace.
33
+
34
+ ## Scope
35
+
36
+ This package initializes the backend service for a complete agent platform:
37
+
38
+ - management API for platform and workspace administration
39
+ - chat-facing API for first-party Chat UI
40
+ - bridge-facing API surface for IM connectors
41
+ - runtime integration points for `ya-agent-sdk`
42
+ - persistence scaffold with PostgreSQL, Redis, packaged Alembic migrations, and startup auto-migration
43
+ - specification documents that define the target architecture before full implementation
44
+
45
+ ## Current Layout
46
+
47
+ ```text
48
+ packages/ya-agent-platform/
49
+ ├── README.md
50
+ ├── infra/
51
+ │ ├── dev.env
52
+ │ └── docker-compose.dev.yml
53
+ ├── pyproject.toml
54
+ ├── spec/
55
+ ├── start.sh
56
+ ├── tests/
57
+ └── ya_agent_platform/
58
+ ├── alembic/
59
+ ├── alembic.ini
60
+ ├── api/
61
+ ├── app.py
62
+ ├── cli.py
63
+ ├── config.py
64
+ ├── db/
65
+ └── redis.py
66
+ ```
67
+
68
+ ## Quick Start
69
+
70
+ From the workspace root:
71
+
72
+ ```bash
73
+ uv sync --all-packages
74
+ make platform-infra-up
75
+ set -a && source packages/ya-agent-platform/infra/dev.env && set +a
76
+ uv run --package ya-agent-platform ya-agent-platform serve --reload
77
+ ```
78
+
79
+ The development server listens on `http://127.0.0.1:9042` by default.
80
+
81
+ ## Database and Redis Commands
82
+
83
+ Use the package CLI directly:
84
+
85
+ ```bash
86
+ uv run --package ya-agent-platform ya-agent-platform migrate
87
+ uv run --package ya-agent-platform ya-agent-platform db current
88
+ uv run --package ya-agent-platform ya-agent-platform db history
89
+ uv run --package ya-agent-platform ya-agent-platform db migrate "add workspace tables"
90
+ ```
91
+
92
+ Use the workspace Makefile wrappers:
93
+
94
+ ```bash
95
+ make platform-db-upgrade
96
+ make platform-db-current
97
+ make platform-db-history
98
+ make platform-db-migrate MSG="add workspace tables"
99
+ ```
100
+
101
+ ## Auto Migration
102
+
103
+ `YA_PLATFORM_AUTO_MIGRATE=true` is the default behavior.
104
+
105
+ - `ya-agent-platform serve` applies migrations before boot when `YA_PLATFORM_DATABASE_URL` is configured
106
+ - `ya-agent-platform migrate` runs migrations separately
107
+ - `start.sh` applies migrations before starting the server in container environments
108
+
109
+ ## Development Infrastructure
110
+
111
+ The dev compose file starts PostgreSQL and Redis:
112
+
113
+ ```bash
114
+ make platform-infra-up
115
+ make platform-infra-status
116
+ make platform-infra-down
117
+ ```
118
+
119
+ Default development URLs live in `packages/ya-agent-platform/infra/dev.env`.
120
+
121
+ ## Combined Docker Image
122
+
123
+ The repository root `Dockerfile` builds a single production image that contains:
124
+
125
+ - the `ya-agent-platform` backend
126
+ - the bundled `ya-agent-platform-web` frontend
127
+ - FastAPI static serving for the built web assets
128
+ - startup auto-migration support through `packages/ya-agent-platform/start.sh`
129
+
130
+ Build locally from the repository root:
131
+
132
+ ```bash
133
+ docker build -t ya-agent-platform:dev .
134
+ ```
135
+
136
+ Run locally:
137
+
138
+ ```bash
139
+ docker run --rm -p 9042:9042 ya-agent-platform:dev
140
+ ```
141
+
142
+ The container serves the combined application on `http://127.0.0.1:9042`.
143
+
144
+ ## Initial API Surface
145
+
146
+ - `GET /healthz` — service health probe with postgres and redis component status
147
+ - `GET /api/v1/platform/info` — platform metadata and enabled surfaces
148
+ - `GET /api/v1/platform/topology` — high-level component topology for the UI and tooling
149
+
150
+ ## Specification Set
151
+
152
+ - [`spec/README.md`](spec/README.md)
153
+ - [`spec/000-platform-overview.md`](spec/000-platform-overview.md)
154
+ - [`spec/001-system-architecture.md`](spec/001-system-architecture.md)
155
+ - [`spec/002-bridge-contract.md`](spec/002-bridge-contract.md)
156
+ - [`spec/003-http-api.md`](spec/003-http-api.md)
157
+
158
+ ## Next Build Phase
159
+
160
+ 1. add persistence models and first migrations
161
+ 2. add runtime orchestration and worker execution
162
+ 3. add bridge registry and delivery guarantees
163
+ 4. connect the web app to live platform endpoints
@@ -0,0 +1,21 @@
1
+ ya_agent_platform/__init__.py,sha256=13C_Udthyw4k6eSBy2nsqqlNRjJAvM16pdqct27E7uA,175
2
+ ya_agent_platform/__main__.py,sha256=2502gK30qurdBEUi2cVKrq5CVAcEGlr7x99dxnoMaew,45
3
+ ya_agent_platform/alembic.ini,sha256=2hXTb5zVfDNd91n5DXfO-DArBWnClJp5i3zIWZivn_c,837
4
+ ya_agent_platform/app.py,sha256=racy4giHeBcFvngCfh-5n34cUYe9c4kghlfhDOeJ6i0,4261
5
+ ya_agent_platform/cli.py,sha256=PWBzu6C5aO6vfOgeZW4esj7qG8eP2f2rQzkPX8bf9d4,3384
6
+ ya_agent_platform/config.py,sha256=FcstDVY3q4gvXXRV5HG-AqJMtMaa1DQwAG0_YcJuraU,1195
7
+ ya_agent_platform/redis.py,sha256=FbX9kghVVh-DpWXDbburyB9m-rTX_KX4C2EBXUEsuBk,238
8
+ ya_agent_platform/alembic/env.py,sha256=z1HUHsCR2A1gk6-g_9TuSJmVHPEdFQYv0aGka0ghE4Q,2020
9
+ ya_agent_platform/alembic/script.py.mako,sha256=LIKpNreaIjWwfG8LSVF9TBkGT4av_lhzQ-yBfo9gYXI,547
10
+ ya_agent_platform/alembic/versions/__init__.py,sha256=CyNpGB0vaUHVDQg2asrD2P5ZfuELZtyHr9J4upBBnOY,56
11
+ ya_agent_platform/api/__init__.py,sha256=da1PTClDMl-IBkrSvq6JC1lnS-K_BASzCvxVhNxN5Ls,13
12
+ ya_agent_platform/api/health.py,sha256=bLZO6l0njKYS1itBjni7fmCjHSpMLzU2xNCyHCk7LKs,1087
13
+ ya_agent_platform/api/platform.py,sha256=3EmgGv1GVqaJW5LFn1XhUSwo1uQKMtfb0ywk4RFhhEo,1785
14
+ ya_agent_platform/db/__init__.py,sha256=beJAosi9jEi_zYoVImwNwOvkFntdfE3ukD1jpF2QwdY,230
15
+ ya_agent_platform/db/base.py,sha256=Fp7n4EfxQOxzqm34-D-rYyu60P88eZUIewCBNP_Iuc4,119
16
+ ya_agent_platform/db/engine.py,sha256=eTt5ofSk3WYTTJAd5qL1OBY9ghbc0NyhNW87JKSCVFo,837
17
+ ya_agent_platform/db/tables.py,sha256=r6gFpJkol_pvWsHag9tP_gKm_6cWUFFnV6egf5elvyA,234
18
+ ya_agent_platform-0.58.3.dist-info/METADATA,sha256=Mp291pGwIWR8oR-fKJtlF5eoNB1C1W5-9PuqHjckYuw,4916
19
+ ya_agent_platform-0.58.3.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
20
+ ya_agent_platform-0.58.3.dist-info/entry_points.txt,sha256=eN6WvrbjfNf7kIBWJ8i1faCS24ZPf73ZRnX4VxxBszA,64
21
+ ya_agent_platform-0.58.3.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ya-agent-platform = ya_agent_platform.cli:cli