chapkit 0.6.4__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.
- chapkit/__init__.py +125 -0
- chapkit/alembic/__init__.py +1 -0
- chapkit/alembic/env.py +76 -0
- chapkit/alembic/script.py.mako +26 -0
- chapkit/alembic/versions/20251010_0927_4d869b5fb06e_initial_schema.py +38 -0
- chapkit/alembic/versions/__init__.py +1 -0
- chapkit/alembic_helpers.py +138 -0
- chapkit/api/__init__.py +64 -0
- chapkit/api/dependencies.py +32 -0
- chapkit/api/service_builder.py +369 -0
- chapkit/artifact/__init__.py +18 -0
- chapkit/artifact/manager.py +132 -0
- chapkit/artifact/models.py +36 -0
- chapkit/artifact/repository.py +48 -0
- chapkit/artifact/router.py +126 -0
- chapkit/artifact/schemas.py +67 -0
- chapkit/cli/__init__.py +5 -0
- chapkit/cli/__main__.py +6 -0
- chapkit/cli/cli.py +47 -0
- chapkit/cli/init.py +209 -0
- chapkit/cli/templates/.gitignore +152 -0
- chapkit/cli/templates/Dockerfile.jinja2 +86 -0
- chapkit/cli/templates/README.md.jinja2 +176 -0
- chapkit/cli/templates/compose.monitoring.yml.jinja2 +90 -0
- chapkit/cli/templates/compose.yml.jinja2 +32 -0
- chapkit/cli/templates/main.py.jinja2 +146 -0
- chapkit/cli/templates/main_shell.py.jinja2 +105 -0
- chapkit/cli/templates/main_task.py.jinja2 +169 -0
- chapkit/cli/templates/monitoring/grafana/dashboards/chapkit-service-metrics.json +1232 -0
- chapkit/cli/templates/monitoring/grafana/provisioning/dashboards/dashboard.yml +13 -0
- chapkit/cli/templates/monitoring/grafana/provisioning/datasources/prometheus.yml +25 -0
- chapkit/cli/templates/monitoring/prometheus/prometheus.yml.jinja2 +13 -0
- chapkit/cli/templates/postman_collection_ml.json.jinja2 +351 -0
- chapkit/cli/templates/postman_collection_ml_shell.json.jinja2 +374 -0
- chapkit/cli/templates/postman_collection_task.json.jinja2 +381 -0
- chapkit/cli/templates/pyproject.toml.jinja2 +15 -0
- chapkit/cli/templates/scripts/predict_model.py.jinja2 +74 -0
- chapkit/cli/templates/scripts/train_model.py.jinja2 +66 -0
- chapkit/config/__init__.py +20 -0
- chapkit/config/manager.py +63 -0
- chapkit/config/models.py +60 -0
- chapkit/config/repository.py +76 -0
- chapkit/config/router.py +139 -0
- chapkit/config/schemas.py +63 -0
- chapkit/data/__init__.py +22 -0
- chapkit/data/dataframe.py +1104 -0
- chapkit/ml/__init__.py +29 -0
- chapkit/ml/manager.py +231 -0
- chapkit/ml/router.py +114 -0
- chapkit/ml/runner.py +271 -0
- chapkit/ml/schemas.py +98 -0
- chapkit/py.typed +0 -0
- chapkit/scheduler.py +154 -0
- chapkit/task/__init__.py +20 -0
- chapkit/task/manager.py +300 -0
- chapkit/task/models.py +20 -0
- chapkit/task/registry.py +46 -0
- chapkit/task/repository.py +31 -0
- chapkit/task/router.py +115 -0
- chapkit/task/schemas.py +28 -0
- chapkit/task/validation.py +76 -0
- chapkit-0.6.4.dist-info/METADATA +231 -0
- chapkit-0.6.4.dist-info/RECORD +65 -0
- chapkit-0.6.4.dist-info/WHEEL +4 -0
- chapkit-0.6.4.dist-info/entry_points.txt +3 -0
chapkit/__init__.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Chapkit - ML/data service modules built on servicekit."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
# Read version from package metadata - must be before internal imports
|
|
8
|
+
try:
|
|
9
|
+
from importlib.metadata import version as _get_version
|
|
10
|
+
|
|
11
|
+
__version__ = _get_version("chapkit")
|
|
12
|
+
except Exception:
|
|
13
|
+
__version__ = "unknown"
|
|
14
|
+
|
|
15
|
+
# CLI feature
|
|
16
|
+
# Scheduler feature
|
|
17
|
+
# Artifact feature
|
|
18
|
+
from .artifact import (
|
|
19
|
+
Artifact,
|
|
20
|
+
ArtifactHierarchy,
|
|
21
|
+
ArtifactIn,
|
|
22
|
+
ArtifactManager,
|
|
23
|
+
ArtifactOut,
|
|
24
|
+
ArtifactRepository,
|
|
25
|
+
ArtifactRouter,
|
|
26
|
+
ArtifactTreeNode,
|
|
27
|
+
)
|
|
28
|
+
from .cli import app as cli_app
|
|
29
|
+
|
|
30
|
+
# Config feature
|
|
31
|
+
from .config import (
|
|
32
|
+
BaseConfig,
|
|
33
|
+
Config,
|
|
34
|
+
ConfigIn,
|
|
35
|
+
ConfigManager,
|
|
36
|
+
ConfigOut,
|
|
37
|
+
ConfigRepository,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Data feature
|
|
41
|
+
from .data import DataFrame, GroupBy
|
|
42
|
+
|
|
43
|
+
# ML feature
|
|
44
|
+
from .ml import (
|
|
45
|
+
FunctionalModelRunner,
|
|
46
|
+
MLManager,
|
|
47
|
+
MLRouter,
|
|
48
|
+
ModelRunnerProtocol,
|
|
49
|
+
PredictionArtifactData,
|
|
50
|
+
PredictRequest,
|
|
51
|
+
PredictResponse,
|
|
52
|
+
TrainedModelArtifactData,
|
|
53
|
+
TrainRequest,
|
|
54
|
+
TrainResponse,
|
|
55
|
+
)
|
|
56
|
+
from .scheduler import ChapkitJobRecord, ChapkitJobScheduler
|
|
57
|
+
|
|
58
|
+
# Task feature
|
|
59
|
+
from .task import (
|
|
60
|
+
Task,
|
|
61
|
+
TaskIn,
|
|
62
|
+
TaskManager,
|
|
63
|
+
TaskOut,
|
|
64
|
+
TaskRegistry,
|
|
65
|
+
TaskRepository,
|
|
66
|
+
TaskRouter,
|
|
67
|
+
validate_and_disable_orphaned_tasks,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_alembic_dir() -> Path:
|
|
72
|
+
"""Get the path to chapkit's bundled alembic migrations directory."""
|
|
73
|
+
return Path(__file__).parent / "alembic"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
__all__ = [
|
|
77
|
+
# Version
|
|
78
|
+
"__version__",
|
|
79
|
+
# Utils
|
|
80
|
+
"get_alembic_dir",
|
|
81
|
+
# CLI
|
|
82
|
+
"cli_app",
|
|
83
|
+
# Scheduler
|
|
84
|
+
"ChapkitJobRecord",
|
|
85
|
+
"ChapkitJobScheduler",
|
|
86
|
+
# Artifact
|
|
87
|
+
"Artifact",
|
|
88
|
+
"ArtifactHierarchy",
|
|
89
|
+
"ArtifactIn",
|
|
90
|
+
"ArtifactManager",
|
|
91
|
+
"ArtifactOut",
|
|
92
|
+
"ArtifactRepository",
|
|
93
|
+
"ArtifactRouter",
|
|
94
|
+
"ArtifactTreeNode",
|
|
95
|
+
# Config
|
|
96
|
+
"BaseConfig",
|
|
97
|
+
"Config",
|
|
98
|
+
"ConfigIn",
|
|
99
|
+
"ConfigManager",
|
|
100
|
+
"ConfigOut",
|
|
101
|
+
"ConfigRepository",
|
|
102
|
+
# Data
|
|
103
|
+
"DataFrame",
|
|
104
|
+
"GroupBy",
|
|
105
|
+
# ML
|
|
106
|
+
"FunctionalModelRunner",
|
|
107
|
+
"MLManager",
|
|
108
|
+
"MLRouter",
|
|
109
|
+
"ModelRunnerProtocol",
|
|
110
|
+
"PredictionArtifactData",
|
|
111
|
+
"PredictRequest",
|
|
112
|
+
"PredictResponse",
|
|
113
|
+
"TrainedModelArtifactData",
|
|
114
|
+
"TrainRequest",
|
|
115
|
+
"TrainResponse",
|
|
116
|
+
# Task
|
|
117
|
+
"Task",
|
|
118
|
+
"TaskIn",
|
|
119
|
+
"TaskManager",
|
|
120
|
+
"TaskOut",
|
|
121
|
+
"TaskRegistry",
|
|
122
|
+
"TaskRepository",
|
|
123
|
+
"TaskRouter",
|
|
124
|
+
"validate_and_disable_orphaned_tasks",
|
|
125
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Chapkit database migrations."""
|
chapkit/alembic/env.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Alembic environment configuration for async SQLAlchemy migrations."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from logging.config import fileConfig
|
|
5
|
+
|
|
6
|
+
from alembic import context
|
|
7
|
+
|
|
8
|
+
# Import the Base metadata from servicekit
|
|
9
|
+
from servicekit import Base
|
|
10
|
+
from sqlalchemy import pool
|
|
11
|
+
from sqlalchemy.engine import Connection
|
|
12
|
+
from sqlalchemy.ext.asyncio import async_engine_from_config
|
|
13
|
+
|
|
14
|
+
# This is the Alembic Config object
|
|
15
|
+
config = context.config
|
|
16
|
+
|
|
17
|
+
# Interpret the config file for Python logging (if present)
|
|
18
|
+
if config.config_file_name is not None:
|
|
19
|
+
fileConfig(config.config_file_name)
|
|
20
|
+
|
|
21
|
+
# Set target metadata for 'autogenerate' support
|
|
22
|
+
target_metadata = Base.metadata
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def run_migrations_offline() -> None:
|
|
26
|
+
"""Run migrations in 'offline' mode (generates SQL scripts)."""
|
|
27
|
+
url = config.get_main_option("sqlalchemy.url")
|
|
28
|
+
context.configure(
|
|
29
|
+
url=url,
|
|
30
|
+
target_metadata=target_metadata,
|
|
31
|
+
literal_binds=True,
|
|
32
|
+
dialect_opts={"paramstyle": "named"},
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
with context.begin_transaction():
|
|
36
|
+
context.run_migrations()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def do_run_migrations(connection: Connection) -> None:
|
|
40
|
+
"""Run migrations with a connection."""
|
|
41
|
+
context.configure(connection=connection, target_metadata=target_metadata)
|
|
42
|
+
|
|
43
|
+
with context.begin_transaction():
|
|
44
|
+
context.run_migrations()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def run_async_migrations() -> None:
|
|
48
|
+
"""Run migrations in async mode using async engine."""
|
|
49
|
+
connectable = async_engine_from_config(
|
|
50
|
+
config.get_section(config.config_ini_section, {}),
|
|
51
|
+
prefix="sqlalchemy.",
|
|
52
|
+
poolclass=pool.NullPool,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
async with connectable.connect() as connection:
|
|
56
|
+
await connection.run_sync(do_run_migrations)
|
|
57
|
+
|
|
58
|
+
await connectable.dispose()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def run_migrations_online() -> None:
|
|
62
|
+
"""Run migrations in 'online' mode (connects to database)."""
|
|
63
|
+
# Since we're being called from Database.init() via run_in_executor,
|
|
64
|
+
# we're in a separate thread and can safely create a new event loop
|
|
65
|
+
loop = asyncio.new_event_loop()
|
|
66
|
+
asyncio.set_event_loop(loop)
|
|
67
|
+
try:
|
|
68
|
+
loop.run_until_complete(run_async_migrations())
|
|
69
|
+
finally:
|
|
70
|
+
loop.close()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if context.is_offline_mode():
|
|
74
|
+
run_migrations_offline()
|
|
75
|
+
else:
|
|
76
|
+
run_migrations_online()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""${message}
|
|
2
|
+
|
|
3
|
+
Revision ID: ${up_revision}
|
|
4
|
+
Revises: ${down_revision | comma,n}
|
|
5
|
+
Create Date: ${create_date}
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
${imports if imports else ""}
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = ${repr(up_revision)}
|
|
14
|
+
down_revision = ${repr(down_revision)}
|
|
15
|
+
branch_labels = ${repr(branch_labels)}
|
|
16
|
+
depends_on = ${repr(depends_on)}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade() -> None:
|
|
20
|
+
"""Apply database schema changes."""
|
|
21
|
+
${upgrades if upgrades else "pass"}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def downgrade() -> None:
|
|
25
|
+
"""Revert database schema changes."""
|
|
26
|
+
${downgrades if downgrades else "pass"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Initial database schema migration."""
|
|
2
|
+
|
|
3
|
+
from alembic import op
|
|
4
|
+
|
|
5
|
+
from chapkit.alembic_helpers import (
|
|
6
|
+
create_artifacts_table,
|
|
7
|
+
create_config_artifacts_table,
|
|
8
|
+
create_configs_table,
|
|
9
|
+
create_tasks_table,
|
|
10
|
+
drop_artifacts_table,
|
|
11
|
+
drop_config_artifacts_table,
|
|
12
|
+
drop_configs_table,
|
|
13
|
+
drop_tasks_table,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# revision identifiers, used by Alembic.
|
|
17
|
+
revision = "4d869b5fb06e"
|
|
18
|
+
down_revision = None
|
|
19
|
+
branch_labels = None
|
|
20
|
+
depends_on = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def upgrade() -> None:
|
|
24
|
+
"""Apply database schema changes."""
|
|
25
|
+
# Chapkit domain tables
|
|
26
|
+
create_artifacts_table(op)
|
|
27
|
+
create_configs_table(op)
|
|
28
|
+
create_config_artifacts_table(op)
|
|
29
|
+
create_tasks_table(op)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def downgrade() -> None:
|
|
33
|
+
"""Revert database schema changes."""
|
|
34
|
+
# Drop in reverse order
|
|
35
|
+
drop_tasks_table(op)
|
|
36
|
+
drop_config_artifacts_table(op)
|
|
37
|
+
drop_configs_table(op)
|
|
38
|
+
drop_artifacts_table(op)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Alembic migration versions."""
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Reusable Alembic migration helpers for chapkit tables.
|
|
2
|
+
|
|
3
|
+
This module provides helper functions for creating and dropping chapkit's database tables
|
|
4
|
+
in Alembic migrations. Using helpers instead of raw Alembic operations provides:
|
|
5
|
+
|
|
6
|
+
- Reusability across migrations
|
|
7
|
+
- Consistent table definitions
|
|
8
|
+
- Clear documentation
|
|
9
|
+
- Easier maintenance
|
|
10
|
+
|
|
11
|
+
Users can create their own helper modules following this pattern for custom tables.
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
# In your migration file
|
|
15
|
+
from chapkit.alembic_helpers import create_configs_table, drop_configs_table
|
|
16
|
+
|
|
17
|
+
def upgrade() -> None:
|
|
18
|
+
create_configs_table(op)
|
|
19
|
+
|
|
20
|
+
def downgrade() -> None:
|
|
21
|
+
drop_configs_table(op)
|
|
22
|
+
|
|
23
|
+
Creating Your Own Helpers:
|
|
24
|
+
Follow the same pattern for your custom tables:
|
|
25
|
+
|
|
26
|
+
# myapp/alembic_helpers.py
|
|
27
|
+
def create_users_table(op: Any) -> None:
|
|
28
|
+
'''Create users table.'''
|
|
29
|
+
op.create_table(
|
|
30
|
+
'users',
|
|
31
|
+
sa.Column('email', sa.String(), nullable=False),
|
|
32
|
+
sa.Column('name', sa.String(), nullable=False),
|
|
33
|
+
sa.Column('id', servicekit.types.ULIDType(length=26), nullable=False),
|
|
34
|
+
sa.Column('created_at', sa.DateTime(), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
|
35
|
+
sa.Column('updated_at', sa.DateTime(), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
|
36
|
+
sa.Column('tags', sa.JSON(), nullable=False, server_default='[]'),
|
|
37
|
+
sa.PrimaryKeyConstraint('id'),
|
|
38
|
+
)
|
|
39
|
+
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=False)
|
|
40
|
+
|
|
41
|
+
def drop_users_table(op: Any) -> None:
|
|
42
|
+
'''Drop users table.'''
|
|
43
|
+
op.drop_index(op.f('ix_users_email'), table_name='users')
|
|
44
|
+
op.drop_table('users')
|
|
45
|
+
|
|
46
|
+
See examples/custom_migrations/ for a complete working example.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
from typing import Any
|
|
50
|
+
|
|
51
|
+
import servicekit.types
|
|
52
|
+
import sqlalchemy as sa
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def create_artifacts_table(op: Any) -> None:
|
|
56
|
+
"""Create artifacts table for hierarchical artifact storage."""
|
|
57
|
+
op.create_table(
|
|
58
|
+
"artifacts",
|
|
59
|
+
sa.Column("parent_id", servicekit.types.ULIDType(length=26), nullable=True),
|
|
60
|
+
sa.Column("data", sa.PickleType(), nullable=False),
|
|
61
|
+
sa.Column("level", sa.Integer(), nullable=False),
|
|
62
|
+
sa.Column("id", servicekit.types.ULIDType(length=26), nullable=False),
|
|
63
|
+
sa.Column("created_at", sa.DateTime(), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
|
64
|
+
sa.Column("updated_at", sa.DateTime(), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
|
65
|
+
sa.Column("tags", sa.JSON(), nullable=False, server_default="[]"),
|
|
66
|
+
sa.ForeignKeyConstraint(["parent_id"], ["artifacts.id"], ondelete="SET NULL"),
|
|
67
|
+
sa.PrimaryKeyConstraint("id"),
|
|
68
|
+
)
|
|
69
|
+
op.create_index(op.f("ix_artifacts_level"), "artifacts", ["level"], unique=False)
|
|
70
|
+
op.create_index(op.f("ix_artifacts_parent_id"), "artifacts", ["parent_id"], unique=False)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def drop_artifacts_table(op: Any) -> None:
|
|
74
|
+
"""Drop artifacts table."""
|
|
75
|
+
op.drop_index(op.f("ix_artifacts_parent_id"), table_name="artifacts")
|
|
76
|
+
op.drop_index(op.f("ix_artifacts_level"), table_name="artifacts")
|
|
77
|
+
op.drop_table("artifacts")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def create_configs_table(op: Any) -> None:
|
|
81
|
+
"""Create configs table for configuration storage."""
|
|
82
|
+
op.create_table(
|
|
83
|
+
"configs",
|
|
84
|
+
sa.Column("name", sa.String(), nullable=False),
|
|
85
|
+
sa.Column("data", sa.JSON(), nullable=False),
|
|
86
|
+
sa.Column("id", servicekit.types.ULIDType(length=26), nullable=False),
|
|
87
|
+
sa.Column("created_at", sa.DateTime(), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
|
88
|
+
sa.Column("updated_at", sa.DateTime(), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
|
89
|
+
sa.Column("tags", sa.JSON(), nullable=False, server_default="[]"),
|
|
90
|
+
sa.PrimaryKeyConstraint("id"),
|
|
91
|
+
)
|
|
92
|
+
op.create_index(op.f("ix_configs_name"), "configs", ["name"], unique=False)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def drop_configs_table(op: Any) -> None:
|
|
96
|
+
"""Drop configs table."""
|
|
97
|
+
op.drop_index(op.f("ix_configs_name"), table_name="configs")
|
|
98
|
+
op.drop_table("configs")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def create_config_artifacts_table(op: Any) -> None:
|
|
102
|
+
"""Create config_artifacts junction table linking configs to artifacts."""
|
|
103
|
+
op.create_table(
|
|
104
|
+
"config_artifacts",
|
|
105
|
+
sa.Column("config_id", servicekit.types.ULIDType(length=26), nullable=False),
|
|
106
|
+
sa.Column("artifact_id", servicekit.types.ULIDType(length=26), nullable=False),
|
|
107
|
+
sa.ForeignKeyConstraint(["artifact_id"], ["artifacts.id"], ondelete="CASCADE"),
|
|
108
|
+
sa.ForeignKeyConstraint(["config_id"], ["configs.id"], ondelete="CASCADE"),
|
|
109
|
+
sa.PrimaryKeyConstraint("config_id", "artifact_id"),
|
|
110
|
+
sa.UniqueConstraint("artifact_id"),
|
|
111
|
+
sa.UniqueConstraint("artifact_id", name="uq_artifact_id"),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def drop_config_artifacts_table(op: Any) -> None:
|
|
116
|
+
"""Drop config_artifacts junction table."""
|
|
117
|
+
op.drop_table("config_artifacts")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def create_tasks_table(op: Any) -> None:
|
|
121
|
+
"""Create tasks table for task execution infrastructure."""
|
|
122
|
+
op.create_table(
|
|
123
|
+
"tasks",
|
|
124
|
+
sa.Column("command", sa.Text(), nullable=False),
|
|
125
|
+
sa.Column("task_type", sa.Text(), nullable=False, server_default="shell"),
|
|
126
|
+
sa.Column("parameters", sa.JSON(), nullable=True),
|
|
127
|
+
sa.Column("enabled", sa.Boolean(), nullable=False, server_default="1"),
|
|
128
|
+
sa.Column("id", servicekit.types.ULIDType(length=26), nullable=False),
|
|
129
|
+
sa.Column("created_at", sa.DateTime(), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
|
130
|
+
sa.Column("updated_at", sa.DateTime(), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
|
131
|
+
sa.Column("tags", sa.JSON(), nullable=False, server_default="[]"),
|
|
132
|
+
sa.PrimaryKeyConstraint("id"),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def drop_tasks_table(op: Any) -> None:
|
|
137
|
+
"""Drop tasks table."""
|
|
138
|
+
op.drop_table("tasks")
|
chapkit/api/__init__.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""FastAPI routers and related presentation logic."""
|
|
2
|
+
|
|
3
|
+
from servicekit.api import CrudPermissions, CrudRouter, Router
|
|
4
|
+
from servicekit.api.middleware import (
|
|
5
|
+
add_error_handlers,
|
|
6
|
+
add_logging_middleware,
|
|
7
|
+
database_error_handler,
|
|
8
|
+
validation_error_handler,
|
|
9
|
+
)
|
|
10
|
+
from servicekit.api.routers import HealthRouter, HealthState, HealthStatus, JobRouter, SystemInfo, SystemRouter
|
|
11
|
+
from servicekit.api.service_builder import ServiceInfo
|
|
12
|
+
from servicekit.api.utilities import build_location_url, run_app
|
|
13
|
+
from servicekit.logging import (
|
|
14
|
+
add_request_context,
|
|
15
|
+
clear_request_context,
|
|
16
|
+
configure_logging,
|
|
17
|
+
get_logger,
|
|
18
|
+
reset_request_context,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from chapkit.artifact import ArtifactRouter
|
|
22
|
+
from chapkit.config import ConfigRouter
|
|
23
|
+
|
|
24
|
+
from .dependencies import get_artifact_manager, get_config_manager
|
|
25
|
+
from .service_builder import AssessedStatus, MLServiceBuilder, MLServiceInfo, ServiceBuilder
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
# Base classes
|
|
29
|
+
"Router",
|
|
30
|
+
"CrudRouter",
|
|
31
|
+
"CrudPermissions",
|
|
32
|
+
# Routers
|
|
33
|
+
"HealthRouter",
|
|
34
|
+
"HealthStatus",
|
|
35
|
+
"HealthState",
|
|
36
|
+
"JobRouter",
|
|
37
|
+
"SystemRouter",
|
|
38
|
+
"SystemInfo",
|
|
39
|
+
"ConfigRouter",
|
|
40
|
+
"ArtifactRouter",
|
|
41
|
+
# Dependencies
|
|
42
|
+
"get_config_manager",
|
|
43
|
+
"get_artifact_manager",
|
|
44
|
+
# Middleware
|
|
45
|
+
"add_error_handlers",
|
|
46
|
+
"add_logging_middleware",
|
|
47
|
+
"database_error_handler",
|
|
48
|
+
"validation_error_handler",
|
|
49
|
+
# Logging
|
|
50
|
+
"configure_logging",
|
|
51
|
+
"get_logger",
|
|
52
|
+
"add_request_context",
|
|
53
|
+
"clear_request_context",
|
|
54
|
+
"reset_request_context",
|
|
55
|
+
# Builders
|
|
56
|
+
"ServiceBuilder",
|
|
57
|
+
"MLServiceBuilder",
|
|
58
|
+
"ServiceInfo",
|
|
59
|
+
"MLServiceInfo",
|
|
60
|
+
"AssessedStatus",
|
|
61
|
+
# Utilities
|
|
62
|
+
"build_location_url",
|
|
63
|
+
"run_app",
|
|
64
|
+
]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Feature-specific FastAPI dependency injection for managers."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
from fastapi import Depends
|
|
6
|
+
from servicekit.api.dependencies import get_session
|
|
7
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
8
|
+
|
|
9
|
+
from chapkit.artifact import ArtifactManager, ArtifactRepository
|
|
10
|
+
from chapkit.config import BaseConfig, ConfigManager, ConfigRepository
|
|
11
|
+
from chapkit.ml import MLManager
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def get_config_manager(session: Annotated[AsyncSession, Depends(get_session)]) -> ConfigManager[BaseConfig]:
|
|
15
|
+
"""Get a config manager instance for dependency injection."""
|
|
16
|
+
repo = ConfigRepository(session)
|
|
17
|
+
return ConfigManager[BaseConfig](repo, BaseConfig)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def get_artifact_manager(session: Annotated[AsyncSession, Depends(get_session)]) -> ArtifactManager:
|
|
21
|
+
"""Get an artifact manager instance for dependency injection."""
|
|
22
|
+
artifact_repo = ArtifactRepository(session)
|
|
23
|
+
return ArtifactManager(artifact_repo)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def get_ml_manager() -> MLManager:
|
|
27
|
+
"""Get an ML manager instance for dependency injection.
|
|
28
|
+
|
|
29
|
+
Note: This is a placeholder. The actual dependency is built by ServiceBuilder
|
|
30
|
+
with the runner in closure, then overridden via app.dependency_overrides.
|
|
31
|
+
"""
|
|
32
|
+
raise RuntimeError("ML manager dependency not configured. Use ServiceBuilder.with_ml() to enable ML operations.")
|