Especificações de hooks¶
Os 10 hookspecs vivem em backend/app/hookspecs.py. Cada
hookspec define o contrato de chamada; os plugins implementam-nos
com @hookimpl. Três deles são variantes de chamada de IA
(síncrona / assíncrona / streaming), lançadas progressivamente
nas versões v1.5.0 e v1.6.0; os restantes permanecem
inalterados desde v0.2.0.
get_assessment_questions¶
@hookspec
def get_assessment_questions(lang: str) -> list[dict] | None:
"""Retorna o pacote de perguntas para o idioma pedido.
Implementado por: plugin assessment.
Modo: list (não firstresult). A rota utiliza atualmente
o resultado do primeiro plugin; uma futura funcionalidade
de "múltiplos pacotes de perguntas" poderia permitir que
os fornecedores registassem pacotes nomeados.
"""
Forma de retorno:
[
{
"id": "q01",
"type": "single" | "multi",
"text": "...",
"answers": [
{"id": "a", "text": "...", "weights": {"deductive": 1.0}},
...
]
},
...
]
calculate_profile¶
@hookspec(firstresult=True)
def calculate_profile(answers: list[dict]) -> dict[str, float]:
"""Agrega as respostas em bruto num perfil de 6 métodos.
Implementado por: plugin assessment.
firstresult: exatamente um plugin calcula o perfil.
"""
Forma de entrada answers:
[
{"question_id": "q01", "answer_ids": ["a", "b"]}, # multi
{"question_id": "q02", "answer_id": "c"}, # single
...
]
Retorno: dict[method, float] com as seis chaves de método.
create_session_prompt¶
@hookspec(firstresult=True)
def create_session_prompt(
project: dict,
profile: dict,
method: str,
step: int,
lang: str,
) -> str:
"""Compõe o prompt do sistema para uma célula (método, passo, idioma).
Implementado por: plugin session.
firstresult: exatamente um plugin compõe o prompt.
"""
Retorno: uma string pronta a ser enviada como mensagem de papel
system. A implementação do plugin session lê a partir do
dicionário _PROMPTS de 42 células e acrescenta um bloco de
contexto.
ai_complete¶
@hookspec(firstresult=True)
def ai_complete(
messages: list[dict[str, Any]],
model: str,
api_key: str,
max_tokens: int = 1024,
) -> str | None:
"""Chama o fornecedor de IA e retorna o texto do assistente.
Implementado por: ai-anthropic, ai-openai, ai-gemini.
firstresult: o plugin do fornecedor correspondente retorna
o texto; os outros retornam None.
"""
Cada plugin de fornecedor verifica o prefixo model
(claude-*, gpt-*, gemini-*) e retorna o texto do
assistente se for proprietário do modelo. Plugins não
correspondentes retornam None, permitindo que firstresult
passe para o seguinte.
Forma de messages (estilo OpenAI):
[
{"role": "system", "content": "..."},
{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."},
{"role": "user", "content": "..."},
]
Os plugins de fornecedor normalizam esta forma para a sua
própria API (Anthropic separa system; Gemini integra-o).
ai_complete_async (v1.5.0+)¶
@hookspec(firstresult=True)
async def ai_complete_async(
messages: list[dict[str, Any]],
model: str,
api_key: str,
max_tokens: int = 1024,
) -> str:
"""Variante aguardável de ai_complete.
Implementado por: ai-anthropic, ai-openai, ai-gemini.
Utilizado na fronteira de ciclo passo 6 -> 7 para que
step-eval + topic-transition sejam disparados
concorrentemente via asyncio.gather
(poupa ~T_2 de latência).
"""
O helper orquestrador call_ai_complete_async prefere
este hook; volta a ai_complete encapsulado em
asyncio.to_thread quando não implementado.
ai_complete_stream (v1.6.0+)¶
@hookspec(firstresult=True)
def ai_complete_stream(
messages: list[dict[str, Any]],
model: str,
api_key: str,
max_tokens: int = 1024,
) -> AsyncIterator[str]:
"""Retorna um iterador assíncrono de deltas de texto.
Implementado por: ai-anthropic, ai-openai, ai-gemini, cada
um usando o streaming assíncrono nativo do SDK do fornecedor.
Alimenta ``POST /api/plugins/session/{id}/message/stream``
(SSE: emite eventos ``start`` / ``chunk`` / ``done``).
"""
recommend_method_switch¶
@hookspec
def recommend_method_switch(
history: list[dict],
profile: dict,
) -> dict | None:
"""Retorna uma recomendação de mudança, ou None.
Implementado por: plugin session.
Modo: list. Os plugins retornam recomendações individuais;
um futuro árbitro escolhe a de maior confiança não-None.
"""
Forma de retorno:
{
"recommended": True,
"to_method": "dialogic",
"reason": "Three sessions of stagnant understanding.",
"confidence": 0.75, # opcional
}
Retornar None (ou {"recommended": False}) quando não é
necessária nenhuma mudança.
on_session_complete¶
@hookspec
def on_session_complete(
session: dict,
rating: dict,
) -> None:
"""Hook de efeito secundário: disparado quando uma sessão termina.
Implementado por: plugin tracking (escreve um ProgressCommit).
Modo: list. Cada subscritor é executado; os erros são
capturados e registados, mas não fazem rollback do
encerramento da sessão.
"""
Os erros neste hook NÃO DEVEM propagar-se — o wrapper
_fire_on_session_complete em backend/app/main.py
captura-os e regista-os.
get_progress_summary¶
@hookspec
def get_progress_summary(
project_id: str,
db: Session,
) -> dict | None:
"""Retorna uma fatia de espaço de nomes do resumo de progresso.
Implementado por: plugin tracking (retorna a fatia de
espaço de nomes "tracking").
Modo: list. As fatias são fundidas superficialmente na rota.
"""
Cada plugin retorna um dict chaveado por um espaço de nomes
único. O plugin tracking retorna
{"tracking": {...}, "step_evaluation": {...}}. Um futuro
plugin de deteção de estagnação poderia retornar
{"stagnation": {...}}, etc.
get_tool_recommendations¶
@hookspec
def get_tool_recommendations(
profile: dict,
lang: str,
limit: int = 5,
) -> list[dict] | None:
"""Retorna recomendações de ferramentas externas classificadas.
Implementado por: plugin tools.
Modo: list. A rota utiliza atualmente o resultado do
primeiro plugin; um futuro recomendador multi-fonte poderia
fundir os resultados.
"""
Forma de retorno:
[
{
"name": "Anki",
"url": "https://apps.ankiweb.net/",
"why": "...",
"weight_keys": ["deductive", "error_based"],
"score": 0.5
},
...
]
firstresult vs modo list¶
| Hookspec | Modo | Porquê |
|---|---|---|
| get_assessment_questions | list (firstresult efetivo) | Atualmente um pacote; futuro poderia registar pacotes nomeados |
| calculate_profile | firstresult | Exatamente um algoritmo |
| create_session_prompt | firstresult | Exatamente um compositor de prompts |
| ai_complete | firstresult | O fornecedor correspondente é proprietário da chamada |
| recommend_method_switch | list | Múltiplos plugins podem sugerir |
| on_session_complete | list | Os efeitos secundários propagam-se |
| get_progress_summary | list | As fatias de espaço de nomes fundem-se |
| get_tool_recommendations | list (firstresult efetivo) | Atualmente uma fonte |
Os modos list que atualmente se comportam como firstresult são
deliberados: o contrato está aberto a extensão multi-plugin,
mas a implementação v0.7.0 utiliza apenas o primeiro.