feat(transcription): add Supabase schema and API endpoints for CIS
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# Transcription module for Classroom Copilot
|
||||
@@ -0,0 +1,53 @@
|
||||
"""Pluggable LLM client for transcription summaries.
|
||||
|
||||
Phase 1: Stub implementation — returns TODO string.
|
||||
Phase 3: Wire up Anthropic, OpenAI, and Ollama providers.
|
||||
"""
|
||||
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
|
||||
async def call_llm(
|
||||
provider: str,
|
||||
model: str,
|
||||
api_key: str,
|
||||
system_prompt: str,
|
||||
user_message: str,
|
||||
) -> str:
|
||||
"""Call an LLM to generate a summary.
|
||||
|
||||
Phase 1 stub — returns a TODO string.
|
||||
Phase 3 will implement actual provider routing.
|
||||
|
||||
Args:
|
||||
provider: 'anthropic', 'openai', 'ollama', 'openrouter', 'google'
|
||||
model: Model name (e.g. 'claude-sonnet-4-6', 'gpt-4o', 'llama3')
|
||||
api_key: User's API key (from localStorage, passed per-request)
|
||||
system_prompt: System prompt template (already filled with transcript)
|
||||
user_message: User message content
|
||||
|
||||
Returns:
|
||||
LLM-generated summary text
|
||||
"""
|
||||
# Phase 1 stub — TODO: implement in Phase 3
|
||||
return f"[TODO: Implement LLM call for provider={provider}, model={model}]"
|
||||
|
||||
|
||||
async def call_anthropic(api_key: str, model: str, system_prompt: str, user_message: str) -> str:
|
||||
"""Call Anthropic Claude API."""
|
||||
# Phase 3 implementation placeholder
|
||||
return f"[TODO: Anthropic call — model={model}]"
|
||||
|
||||
|
||||
async def call_openai(api_key: str, model: str, system_prompt: str, user_message: str) -> str:
|
||||
"""Call OpenAI API."""
|
||||
# Phase 3 implementation placeholder
|
||||
return f"[TODO: OpenAI call — model={model}]"
|
||||
|
||||
|
||||
async def call_ollama(api_key: str, model: str, system_prompt: str, user_message: str) -> str:
|
||||
"""Call local Ollama instance."""
|
||||
# Phase 3 implementation placeholder
|
||||
ollama_url = os.getenv("OLLAMA_URL", "https://ollama.kevlarai.com")
|
||||
return f"[TODO: Ollama call — url={ollama_url}, model={model}]"
|
||||
@@ -0,0 +1,208 @@
|
||||
"""Pydantic models for the Transcription system."""
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# --- Session Models ---
|
||||
|
||||
class TranscriptionSessionCreate(BaseModel):
|
||||
user_id: str
|
||||
title: Optional[str] = None
|
||||
canvas_type: str = "teaching-canvas"
|
||||
|
||||
|
||||
class TranscriptionSessionUpdate(BaseModel):
|
||||
title: Optional[str] = None
|
||||
ended_at: Optional[datetime] = None
|
||||
timetable_period_id: Optional[str] = None
|
||||
timetable_event_type: Optional[str] = None
|
||||
timetable_event_label: Optional[str] = None
|
||||
auto_tagged: Optional[bool] = None
|
||||
llm_provider: Optional[str] = None
|
||||
llm_model: Optional[str] = None
|
||||
|
||||
|
||||
class TranscriptionSessionResponse(BaseModel):
|
||||
id: str
|
||||
user_id: str
|
||||
title: Optional[str] = None
|
||||
canvas_type: str
|
||||
started_at: datetime
|
||||
ended_at: Optional[datetime] = None
|
||||
duration_seconds: Optional[int] = None
|
||||
timetable_period_id: Optional[str] = None
|
||||
timetable_event_type: Optional[str] = None
|
||||
timetable_event_label: Optional[str] = None
|
||||
auto_tagged: bool = False
|
||||
llm_provider: Optional[str] = None
|
||||
llm_model: Optional[str] = None
|
||||
word_count: int = 0
|
||||
segment_count: int = 0
|
||||
metadata: dict = {}
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class SessionListResponse(BaseModel):
|
||||
sessions: List[TranscriptionSessionResponse]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
|
||||
|
||||
# --- Segment Models ---
|
||||
|
||||
class TranscriptionSegmentCreate(BaseModel):
|
||||
session_id: str
|
||||
sequence_index: int
|
||||
text: str
|
||||
start_seconds: float
|
||||
end_seconds: float
|
||||
is_final: bool = True
|
||||
speaker_label: Optional[str] = None
|
||||
keyword_matches: Optional[List[str]] = None
|
||||
|
||||
|
||||
class TranscriptionSegmentResponse(BaseModel):
|
||||
id: str
|
||||
session_id: str
|
||||
sequence_index: int
|
||||
text: str
|
||||
start_seconds: float
|
||||
end_seconds: float
|
||||
is_final: bool = True
|
||||
speaker_label: Optional[str] = None
|
||||
keyword_matches: Optional[List[str]] = None
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# --- Canvas Event Models ---
|
||||
|
||||
class CanvasEventCreate(BaseModel):
|
||||
session_id: Optional[str] = None
|
||||
user_id: str
|
||||
timestamp: Optional[datetime] = None
|
||||
session_elapsed_seconds: Optional[float] = None
|
||||
event_type: str
|
||||
event_payload: dict = {}
|
||||
canvas_snapshot_url: Optional[str] = None
|
||||
tldraw_page_id: Optional[str] = None
|
||||
tldraw_shape_ids: Optional[List[str]] = None
|
||||
|
||||
|
||||
class CanvasEventResponse(BaseModel):
|
||||
id: str
|
||||
session_id: Optional[str] = None
|
||||
user_id: str
|
||||
timestamp: datetime
|
||||
session_elapsed_seconds: Optional[float] = None
|
||||
event_type: str
|
||||
event_payload: dict = {}
|
||||
canvas_snapshot_url: Optional[str] = None
|
||||
tldraw_page_id: Optional[str] = None
|
||||
tldraw_shape_ids: Optional[List[str]] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# --- Summary Models ---
|
||||
|
||||
class SummaryGenerateRequest(BaseModel):
|
||||
summary_type: str # full_lesson, questions_asked, teaching_style, key_moments, segment
|
||||
provider: str # anthropic, openai, ollama, openrouter, google
|
||||
model: str
|
||||
api_key: str # from frontend user settings, passed per-request
|
||||
segment_range: Optional[List[Optional[int]]] = None # [start, end], null = all
|
||||
include_canvas_snapshots: bool = False
|
||||
|
||||
|
||||
class SummaryResponse(BaseModel):
|
||||
id: str
|
||||
session_id: str
|
||||
user_id: str
|
||||
summary_type: str
|
||||
content: str
|
||||
prompt_used: Optional[str] = None
|
||||
llm_provider: str
|
||||
llm_model: str
|
||||
input_tokens: Optional[int] = None
|
||||
output_tokens: Optional[int] = None
|
||||
segment_range_start: Optional[int] = None
|
||||
segment_range_end: Optional[int] = None
|
||||
canvas_snapshot_urls: Optional[List[str]] = None
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# --- Keyword Watch Models ---
|
||||
|
||||
class KeywordWatchCreate(BaseModel):
|
||||
user_id: str
|
||||
keyword: str
|
||||
match_type: str = "contains" # contains, exact, starts_with, regex
|
||||
action: str = "log" # log, alert, canvas_shape, webhook
|
||||
|
||||
|
||||
class KeywordWatchResponse(BaseModel):
|
||||
id: str
|
||||
user_id: str
|
||||
keyword: str
|
||||
match_type: str = "contains"
|
||||
action: str = "log"
|
||||
is_active: bool = True
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# --- Keyword Event Models ---
|
||||
|
||||
class KeywordEventCreate(BaseModel):
|
||||
session_id: str
|
||||
segment_id: Optional[str] = None
|
||||
keyword_watch_id: Optional[str] = None
|
||||
keyword_text: str
|
||||
matched_in_text: str
|
||||
session_elapsed_seconds: Optional[float] = None
|
||||
|
||||
|
||||
class KeywordEventResponse(BaseModel):
|
||||
id: str
|
||||
session_id: str
|
||||
segment_id: Optional[str] = None
|
||||
keyword_watch_id: Optional[str] = None
|
||||
keyword_text: str
|
||||
matched_in_text: str
|
||||
session_elapsed_seconds: Optional[float] = None
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# --- Export Models ---
|
||||
|
||||
class ExportFormat(BaseModel):
|
||||
format: str # srt, txt, json
|
||||
|
||||
|
||||
# --- Timetable Models ---
|
||||
|
||||
class CurrentPeriodResponse(BaseModel):
|
||||
period_id: Optional[str] = None
|
||||
event_type: Optional[str] = None
|
||||
event_label: Optional[str] = None
|
||||
start_time: Optional[datetime] = None
|
||||
end_time: Optional[datetime] = None
|
||||
@@ -0,0 +1,51 @@
|
||||
"""LLM prompt templates for transcription summaries."""
|
||||
|
||||
FULL_LESSON = """You are an expert educational analyst. Below is a transcript of a lesson. Provide a structured summary including:
|
||||
1. Main topics covered (with estimated time on each)
|
||||
2. Key teaching moments
|
||||
3. Notable observations about pacing and engagement
|
||||
4. Suggestions for improvement
|
||||
|
||||
Transcript:
|
||||
{transcript}"""
|
||||
|
||||
QUESTIONS_ASKED = """You are an expert educational analyst. Extract all questions asked by the teacher from this lesson transcript. For each question:
|
||||
1. Quote the exact question
|
||||
2. Categorize by type: open/closed
|
||||
3. Identify Bloom's taxonomy level (Remember, Understand, Apply, Analyze, Evaluate, Create)
|
||||
4. Note any subject-specific content
|
||||
|
||||
Transcript:
|
||||
{transcript}"""
|
||||
|
||||
TEACHING_STYLE = """You are an expert educational analyst. Analyse this lesson transcript for teaching style. Comment on:
|
||||
1. Pacing — was the lesson well-paced? Where did it drag or rush?
|
||||
2. Questioning technique — variety, depth, follow-up
|
||||
3. Explanation clarity — were concepts explained effectively?
|
||||
4. Student engagement indicators (changes in tone, pauses for responses)
|
||||
5. Suggestions for improvement
|
||||
|
||||
Transcript:
|
||||
{transcript}"""
|
||||
|
||||
KEY_MOMENTS = """You are an expert educational analyst. Identify the most significant moments in this lesson:
|
||||
1. Topic transitions (with timestamps)
|
||||
2. Student interactions (marked by change in tone or pause)
|
||||
3. Key explanations that seemed to land well
|
||||
4. Any moments of confusion or breakthrough
|
||||
|
||||
Transcript:
|
||||
{transcript}"""
|
||||
|
||||
SEGMENT = """Summarise this portion of the lesson in 2-3 sentences suitable for a lesson log entry.
|
||||
|
||||
Transcript:
|
||||
{transcript}"""
|
||||
|
||||
PROMPT_TEMPLATES = {
|
||||
"full_lesson": FULL_LESSON,
|
||||
"questions_asked": QUESTIONS_ASKED,
|
||||
"teaching_style": TEACHING_STYLE,
|
||||
"key_moments": KEY_MOMENTS,
|
||||
"segment": SEGMENT,
|
||||
}
|
||||
Reference in New Issue
Block a user