Post-v1: per-RFC model availability (UX half) folded into §6.6
First §19.2 candidate settled after v1. The heavier per-RFC-model
topic subdivided into UX (this) and credential delegation + funder
role (still §19.2). New §6.6 carries the rule: an optional `models:`
frontmatter field on the meta-repo RFC entry; absent inherits the
operator universe, populated narrows the picker to the intersection
with provisioned providers, `[]` opts the RFC out of AI entirely.
The first resolved entry is the RFC default. §18's ENABLED_MODELS is
reframed as the operator universe.
Code: migration 009 adds nullable cached_rfcs.models_json (NULL ≠ []
is load-bearing); entry.py grows the optional field with absent-vs-
empty round-tripping in parse/serialize; new models_resolver module
holds the rule; api_branches replaces /api/models with the slug-aware
/api/rfcs/{slug}/models and threads the chat + reask paths through
the resolver; api_prs §10.2 uses the resolver and extends the stub
fallback to the opt-out case; frontend passes slug to listModels.
Tests 106/106 green (96 prior + 10 in test_per_rfc_models.py). No
behavioral change for entries without `models:` — operator universe
preserved as default.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+10
-9
@@ -23,7 +23,7 @@ from typing import Any
|
||||
from fastapi import APIRouter, HTTPException, Request
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from . import auth, cache, chat as chat_layer, db, entry as entry_mod
|
||||
from . import auth, cache, chat as chat_layer, db, entry as entry_mod, models_resolver
|
||||
from .bot import Bot
|
||||
from .config import Config
|
||||
from .gitea import Gitea, GiteaError
|
||||
@@ -68,13 +68,13 @@ def make_router(
|
||||
) -> APIRouter:
|
||||
router = APIRouter()
|
||||
|
||||
default_model = next(iter(providers)) if providers else ""
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# §10.2: AI-drafted PR title and description.
|
||||
# Returned ahead of submit so the modal renders with prefilled values
|
||||
# the contributor can edit. The contributor's gesture is what
|
||||
# produces the open-pr call; the draft is just a starting point.
|
||||
# Per §6.6 the model used is the RFC's resolved default; an empty
|
||||
# resolved list falls back to the deterministic stub.
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
@router.post("/api/rfcs/{slug}/branches/{branch:path}/pr-draft")
|
||||
@@ -90,9 +90,10 @@ def make_router(
|
||||
if not branch_fetched:
|
||||
raise HTTPException(404, f"Branch {path} not found")
|
||||
chat_messages = _branch_chat_excerpt(slug, branch)
|
||||
rfc_default_model = models_resolver.default_model_for_rfc(slug, providers)
|
||||
title, description = _draft_with_provider(
|
||||
providers=providers,
|
||||
default_model=default_model,
|
||||
default_model=rfc_default_model,
|
||||
rfc_title=rfc["title"],
|
||||
main_body=_extract_body(rfc, (main_fetched or ("", ""))[0]),
|
||||
branch_body=_extract_body(rfc, branch_fetched[0]),
|
||||
@@ -795,12 +796,12 @@ def _draft_with_provider(
|
||||
"""Per §10.2: AI-drafted title (spec voice) and description (2–4
|
||||
sentences pulling from chat).
|
||||
|
||||
When no provider is configured we fall back to a deterministic
|
||||
stub — the surface still works; the contributor just edits the
|
||||
text. The fallback also matches the test seam where Slice 3
|
||||
integration tests don't always inject a fake provider.
|
||||
When no provider is configured — or per §6.6 the RFC's resolved
|
||||
list is empty (operator universe empty, frontmatter opt-out, or
|
||||
intersection empty) — we fall back to a deterministic stub. The
|
||||
surface still works; the contributor edits the text.
|
||||
"""
|
||||
if not providers:
|
||||
if not providers or not default_model:
|
||||
return _stub_draft(rfc_title=rfc_title, main_body=main_body, branch_body=branch_body)
|
||||
provider = providers.get(default_model) or next(iter(providers.values()))
|
||||
system = (
|
||||
|
||||
Reference in New Issue
Block a user