c6b1e72130
The operator decides which model providers exist (env / registry — Anthropic,
OpenAI, x.AI, Ollama, several at once). The *tree owner* decides who uses which:
- Members' assistant -> one configured provider (or none)
- Recommender (association/connection finder) -> one configured provider (or none)
- Owner -> may use any configured provider
Backend: two nullable columns on `trees` (ai_member_provider,
ai_recommender_provider) + migration; `configured_llm_providers()` exposes the
registry as {name, model} with no secrets; owner-gated GET/PATCH
/trees/{id}/ai validate names against the configured set. Frontend: owner-only
"AI models" page with a dropdown per role, graceful 403 for non-owners, and a
sidebar link.
Per-model-within-a-provider selection is a follow-up; today each provider maps
to its single configured model.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Justin Paul <justin@jpaul.me>
27 lines
759 B
Python
27 lines
759 B
Python
"""tree AI model policy (ai_member_provider, ai_recommender_provider)
|
|
|
|
Revision ID: b2c3d4e5f6a7
|
|
Revises: a1b2c3d4e5f6
|
|
Create Date: 2026-06-09
|
|
|
|
"""
|
|
from collections.abc import Sequence
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision: str = "b2c3d4e5f6a7"
|
|
down_revision: str | None = "a1b2c3d4e5f6"
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column("trees", sa.Column("ai_member_provider", sa.String(length=32), nullable=True))
|
|
op.add_column("trees", sa.Column("ai_recommender_provider", sa.String(length=32), nullable=True))
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_column("trees", "ai_recommender_provider")
|
|
op.drop_column("trees", "ai_member_provider")
|