For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Replace the minimal dialogue system prompt with a structured, grade-aware template; wire the existing curriculum-grade dropdown + learning-goal vocabulary/sentences into session creation so the backend can render the full prompt at session creation time.
Architecture: Frontend persists grade (= grade.id from curriculum.json, e.g. 'grade5-1') into the Pinia speaking store and sends it along with vocabulary[] and sentences[] to the backend's POST /session. Backend renders a full multi-tier prompt template into dialogue_session.system_prompt (no schema change). The LLM reads the literal {年级} value and self-selects the correct tier from the three Grade-Specific Adaptation Rules sections in the prompt.
Tech Stack:
/Users/buoy/Development/gitrepo/PPT)/Users/buoy/Development/gitrepo/cococlass-english-speaking-api)Spec: docs/superpowers/specs/2026-04-25-english-speaking-dialogue-prompt-design.md
Backend (cococlass-english-speaking-api):
app/api/dialogue.py:44-49 — extend CreateSessionRequestapp/api/dialogue.py:52-68 — pass new fields to serviceapp/service/speaking/dialogue_service.py:27-34 — replace SYSTEM_PROMPT_TEMPLATEapp/service/speaking/dialogue_service.py:52-90 — extend create_session_onlytests/api/test_dialogue_greeting.py — extend existing test or add a new filetests/service/speaking/test_dialogue_service_prompt.py — new file for prompt renderingFrontend (PPT):
src/types/englishSpeaking.ts:51-57 — add grade to TopicDiscussionConfigsrc/types/englishSpeaking.ts:240-246 — add grade to SessionConfigsrc/store/speaking.ts:10-42 — add grade to DEFAULT_CONFIG, add updateGradesrc/views/Editor/EnglishSpeaking/SpeakingPanel.vue:80-82 — bind selectedGrade to storesrc/views/Editor/EnglishSpeaking/preview/TopicDiscussionPreview.vue:213-218 — pass grade/sentencessrc/views/Editor/EnglishSpeaking/services/llmService.ts:155-176 — send grade/vocabulary/sentencesUntouched:
app/models/dialogue.py (no schema change)src/views/Editor/EnglishSpeaking/components/CurriculumSelector.vue (already emits grade.id)src/views/Editor/EnglishSpeaking/preview/DialogueChatView.vuesrc/views/Editor/EnglishSpeaking/composables/useDialogueEngine.tsBackend first (Tasks 1–4) so the API accepts new fields before frontend sends them. Frontend after (Tasks 5–9). Then integration verification (Task 10).
CreateSessionRequest Pydantic modelRepo: /Users/buoy/Development/gitrepo/cococlass-english-speaking-api
Files:
Modify: app/api/dialogue.py:44-49
[ ] Step 1: Update the Pydantic model
Edit app/api/dialogue.py, replace the existing CreateSessionRequest definition:
class CreateSessionRequest(BaseModel):
topic: str
grade: str
vocabulary: list[str] = []
sentences: list[str] = []
totalRounds: int = 3
durationSeconds: int | None = None
roleId: str | None = None
userId: str | None = None
Run: cd /Users/buoy/Development/gitrepo/cococlass-english-speaking-api && python -c "from app.api.dialogue import CreateSessionRequest; r = CreateSessionRequest(topic='t', grade='g'); print(r.vocabulary, r.sentences)"
Expected output: [] []
cd /Users/buoy/Development/gitrepo/cococlass-english-speaking-api
git add app/api/dialogue.py
git commit -m "feat(speaking): add grade/vocabulary/sentences to CreateSessionRequest"
SYSTEM_PROMPT_TEMPLATERepo: /Users/buoy/Development/gitrepo/cococlass-english-speaking-api
Files:
Modify: app/service/speaking/dialogue_service.py:27-34
[ ] Step 1: Replace the template constant
In app/service/speaking/dialogue_service.py, replace the SYSTEM_PROMPT_TEMPLATE = """...""" block (currently lines 27–34) with the full multi-tier template. Use Python .format() named-substitution style — placeholders are {年级}, {话题主题}, {重点词汇}, {重点句型}. Note: any literal { / } in the template body (none expected) would need doubling.
SYSTEM_PROMPT_TEMPLATE = """You are a professional English speaking practice partner designed for students at the {年级}. Your goal is to create a relaxed and encouraging learning environment to help students improve their spoken English skills. All output must be in English.
## Core Configuration
- **Practice Level**: {年级}
- **Conversation Topic**: {话题主题}
- **Key Vocabulary**: {重点词汇}
- **Key Sentence Patterns**: {重点句型}
## Grade-Specific Adaptation Rules
**The system must dynamically adjust all language output based on the following parameters for the specified {年级}:**
### For Grades 1-3 (e.g., Grade 2)
* **Vocabulary Selection**: 100% CEFR A1-A2 level words. Use only the most common, concrete terms related to the topic.
* **Sentence Complexity**: 100% simple sentences (single subject + predicate). Use no compound or complex sentences.
* **Sentence Length**: Aim for 4 to 8 words per sentence.
* **Utterances per Turn**: As the teacher, you should speak 1 sentence per turn. Encourage the student to respond with 1 simple sentence.
* **Pacing & Scaffolding**: Speak slowly. Use repetition. Ask one simple, direct question at a time.
### For Grades 4-6 (e.g., Grade 5)
* **Vocabulary Selection**: Approximately 80% CEFR A1-A2 and 20% B1-B2 level words. You may introduce a few more descriptive or topic-specific adjectives/adverbs.
* **Sentence Complexity**: Approximately 90% simple sentences and 10% compound sentences (using 'and', 'but', 'so', 'because').
* **Sentence Length**: Aim for 10 to 15 words per sentence.
* **Utterances per Turn**: As the teacher, you can use 1-2 sentences per turn. Encourage the student to form slightly longer responses.
* **Pacing & Scaffolding**: Use natural pacing. You can connect ideas within a turn using basic conjunctions.
### For Middle School Grades 1-3 (e.g., Grade 8)
* **Vocabulary Selection**: Approximately 70% CEFR A1-A2 and 30% B1-B2 level words. Confidently use a wider range of vocabulary, including more abstract or general academic terms where appropriate.
* **Sentence Complexity**: Approximately 60% simple sentences and 40% compound/complex sentences. Use relative clauses, participial phrases, and a variety of conjunctions.
* **Sentence Length**: Aim for 15 to 25 words per sentence.
* **Utterances per Turn**: As the teacher, you can use 2 sentences to model richer language. Encourage the student to express opinions, reasons, and sequences of events.
* **Pacing & Scaffolding**: Use natural, conversational pacing. Prompt for elaboration (e.g., "Why?", "Can you tell me more?").
## Conversation Rules
1. **Role & Tone**: You are the friendly tutor. Maintain a positive, patient, and encouraging tone. Correct errors gently and implicitly by modeling the correct form in your following response.
2. **Topic Adherence**: All dialogue must naturally revolve around the topic: **{话题主题}**. Gently guide the conversation back if it drifts.
3. **Language Focus Integration**:
* Seamlessly incorporate the **{重点词汇}** into the conversation. Aim to use and elicit each target word multiple times.
* Create scenarios that naturally prompt the use of the **{重点句型}**.
* Provide positive, specific feedback when the student successfully uses a target word or pattern (e.g., "Great! You said 'by bus.'").
4. **Conversation Flow**:
* **Start**: Greet, introduce yourself, and state the topic.
* **Core**: Engage in 4-6 conversational turns. Adapt your follow-up questions and prompts based on the student's responses and the **Grade-Specific Rules** above.
* **End**: Conclude the practice naturally with encouraging feedback.
## Example Dialogue Styles
* **Grade 2 Example**: T: "Hello. Let's talk about school. How do you come to school?" S: "I come by car." T: "Good! By car. Is it fun?"
* **Grade 5 Example**: T: "Hello! Today, let's discuss how we get to school. How do you usually come to school?" S: "I usually come by bus with my friend." T: "That's nice. Do you live near the bus stop?"
* **Grade 8 Example**: T: "Hello there. Let's talk about our commutes to school. What's your typical way of getting to school, and how long does it usually take?" S: "I typically ride my bike because I live quite near, so it only takes about ten minutes." T: "That's efficient. Do you ever use other means of transport, like the subway?"
## Start the Conversation Now
Initiate the conversation. Your first utterance must:
1. Be in English.
2. Start with a greeting like "Hello" or "Hi".
3. Mention the topic **{话题主题}**.
4. Be crafted according to the **Grade-Specific Adaptation Rules** for the specified **{年级}**.
"""
Run: cd /Users/buoy/Development/gitrepo/cococlass-english-speaking-api && python -c "from app.service.speaking.dialogue_service import SYSTEM_PROMPT_TEMPLATE; print(SYSTEM_PROMPT_TEMPLATE.format(年级='grade5-1', 话题主题='How I get to school', 重点词汇='by bus, on foot', 重点句型='- How do you come to school?')[:200])"
Expected: First 200 chars of the rendered prompt with grade5-1 and How I get to school substituted in. No KeyError.
git add app/service/speaking/dialogue_service.py
git commit -m "feat(speaking): replace system prompt with grade-aware multi-tier template"
create_session_only and add unit testRepo: /Users/buoy/Development/gitrepo/cococlass-english-speaking-api
Files:
app/service/speaking/dialogue_service.py:52-90Create: tests/service/speaking/test_dialogue_service_prompt.py
[ ] Step 1: Write the failing test
Create tests/service/speaking/test_dialogue_service_prompt.py:
"""Tests for system_prompt rendering in create_session_only."""
from unittest.mock import MagicMock
import pytest
import pytest_asyncio
from fastapi import HTTPException
from sqlalchemy import select
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from sqlalchemy.pool import StaticPool
from app.models.dialogue import Base, DialogueSession
from app.service.speaking.dialogue_service import DialogueService
@pytest_asyncio.fixture
async def db():
engine = create_async_engine(
"sqlite+aiosqlite:///:memory:",
poolclass=StaticPool,
connect_args={"check_same_thread": False},
)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
SessionLocal = async_sessionmaker(engine, expire_on_commit=False)
async with SessionLocal() as s:
yield s
@pytest.fixture
def service():
return DialogueService(
asr=MagicMock(), llm=MagicMock(), assessor=MagicMock(), storage=MagicMock(),
)
@pytest.mark.asyncio
async def test_create_session_renders_full_prompt(db, service):
await service.create_session_only(
db=db,
topic="How I get to school",
grade="grade5-1",
vocabulary=["by bus", "on foot"],
sentences=["How do you come to school?", "I come by bus."],
)
session = (await db.execute(select(DialogueSession))).scalar_one()
prompt = session.system_prompt
assert "grade5-1" in prompt
assert "How I get to school" in prompt
assert "by bus, on foot" in prompt
assert "- How do you come to school?" in prompt
assert "- I come by bus." in prompt
assert "Grade-Specific Adaptation Rules" in prompt
assert "For Grades 1-3" in prompt
assert "For Grades 4-6" in prompt
assert "For Middle School" in prompt
@pytest.mark.asyncio
async def test_create_session_handles_empty_arrays(db, service):
await service.create_session_only(
db=db,
topic="X",
grade="grade5-1",
vocabulary=[],
sentences=[],
)
session = (await db.execute(select(DialogueSession))).scalar_one()
prompt = session.system_prompt
# Placeholders are substituted (with empty strings) — no leftover braces
assert "{重点词汇}" not in prompt
assert "{重点句型}" not in prompt
@pytest.mark.asyncio
async def test_create_session_rejects_blank_grade(db, service):
with pytest.raises(HTTPException) as exc:
await service.create_session_only(
db=db,
topic="X",
grade=" ",
vocabulary=[],
sentences=[],
)
assert exc.value.status_code == 400
Run: cd /Users/buoy/Development/gitrepo/cococlass-english-speaking-api && pytest tests/service/speaking/test_dialogue_service_prompt.py -v
Expected: FAIL — create_session_only() doesn't accept grade/vocabulary/sentences yet (TypeError on unexpected kwarg).
create_session_only signature and renderingIn app/service/speaking/dialogue_service.py, replace the existing create_session_only method (lines 52–90) with:
async def create_session_only(
self,
db: AsyncSession,
topic: str,
grade: str,
vocabulary: list[str],
sentences: list[str],
total_rounds: int = 3,
duration_seconds: int | None = None,
role_config: dict | None = None,
user_id: str | None = None,
) -> dict:
"""只建会话记录,不调 LLM / Insert a session row without invoking the LLM.
Returns:
dict: {sessionId, totalRounds, currentRound, expiresAt}
"""
from fastapi import HTTPException
if not grade.strip():
raise HTTPException(status_code=400, detail="grade is required")
rendered_vocab = ", ".join(vocabulary)
rendered_sentences = "\n".join(f"- {s}" for s in sentences)
system_prompt = SYSTEM_PROMPT_TEMPLATE.format(
年级=grade,
话题主题=topic,
重点词汇=rendered_vocab,
重点句型=rendered_sentences,
)
expires_at = None
if duration_seconds:
expires_at = datetime.now() + timedelta(seconds=duration_seconds)
session = DialogueSession(
user_id=user_id,
topic=topic,
role_config=role_config,
total_rounds=total_rounds,
current_round=1,
status="active",
system_prompt=system_prompt,
expires_at=expires_at,
)
db.add(session)
await db.commit()
await db.refresh(session)
return {
"sessionId": session.uuid,
"totalRounds": total_rounds,
"currentRound": session.current_round,
"expiresAt": expires_at.isoformat() if expires_at else None,
}
Run: cd /Users/buoy/Development/gitrepo/cococlass-english-speaking-api && pytest tests/service/speaking/test_dialogue_service_prompt.py -v
Expected: All 3 tests PASS.
Run: cd /Users/buoy/Development/gitrepo/cococlass-english-speaking-api && pytest -v 2>&1 | tail -40
Expected: Existing tests may fail because they call create_session_only without the new required grade/vocabulary/sentences arguments. Note the failures — they're addressed in Task 4 (route handler update) and may also need fixture updates. If only the route's E2E test is broken, that's expected; we'll fix it in Task 4. If a service-level test breaks, fix the call site to pass the new required args (use defaults like grade='grade5-1', vocabulary=[], sentences=[]).
git add app/service/speaking/dialogue_service.py tests/service/speaking/test_dialogue_service_prompt.py
git commit -m "feat(speaking): render grade-aware system_prompt in create_session_only"
Repo: /Users/buoy/Development/gitrepo/cococlass-english-speaking-api
Files:
app/api/dialogue.py:52-68 (the /session POST handler)Modify: tests/api/test_dialogue_greeting.py (existing E2E test sends old payload)
[ ] Step 1: Update the route handler to pass new fields
In app/api/dialogue.py, replace the body of the create_session route handler:
@router.post("/session")
async def create_session(
req: CreateSessionRequest,
db: AsyncSession = Depends(get_db),
service: DialogueService = Depends(get_dialogue_service),
):
"""创建对话 session(不生成开场白)。开场白由 POST /session/{id}/greeting 触发。"""
logger.info(f"Creating session: topic={req.topic}, grade={req.grade}, rounds={req.totalRounds}")
result = await service.create_session_only(
db=db,
topic=req.topic,
grade=req.grade,
vocabulary=req.vocabulary,
sentences=req.sentences,
total_rounds=req.totalRounds,
duration_seconds=req.durationSeconds,
user_id=req.userId,
)
logger.info(f"Session created: {result['sessionId']}")
return result
tests/api/test_dialogue_greeting.pyFind every client.post("/api/speaking/dialogue/session", json=...) call site (or whatever path your test uses — search the file). For each one, add "grade": "grade5-1" to the JSON body. If vocabulary / sentences aren't sent, they default to [] per the Pydantic model — no change needed.
Concretely, run: grep -n '"topic"' /Users/buoy/Development/gitrepo/cococlass-english-speaking-api/tests/api/test_dialogue_greeting.py
Then for each match, ensure the JSON dict includes "grade": "grade5-1".
Append to tests/api/test_dialogue_greeting.py (inside the existing test module — reuse the test_env fixture):
@pytest.mark.asyncio
async def test_create_session_requires_grade(test_env):
client: AsyncClient = test_env["client"]
res = await client.post(
"/api/speaking/dialogue/session",
json={"topic": "Animals"}, # missing 'grade'
)
assert res.status_code == 422 # Pydantic validation error
@pytest.mark.asyncio
async def test_create_session_persists_grade_in_prompt(test_env):
client: AsyncClient = test_env["client"]
res = await client.post(
"/api/speaking/dialogue/session",
json={
"topic": "How I get to school",
"grade": "grade5-1",
"vocabulary": ["by bus", "on foot"],
"sentences": ["How do you come to school?"],
},
)
assert res.status_code == 200
session_id = res.json()["sessionId"]
# Inspect persisted prompt
SessionLocal = test_env["SessionLocal"]
from sqlalchemy import select
from app.models.dialogue import DialogueSession
async with SessionLocal() as s:
row = (await s.execute(select(DialogueSession).where(DialogueSession.uuid == session_id))).scalar_one()
assert "grade5-1" in row.system_prompt
assert "by bus, on foot" in row.system_prompt
assert "- How do you come to school?" in row.system_prompt
If the existing test_env fixture doesn't expose client and SessionLocal as a dict, adapt to whatever shape it returns (read the fixture body around lines 22–60). The point: have client for HTTP calls and a way to query the DB.
Run: cd /Users/buoy/Development/gitrepo/cococlass-english-speaking-api && pytest tests/api/test_dialogue_greeting.py -v
Expected: All tests PASS, including the new ones.
Run: cd /Users/buoy/Development/gitrepo/cococlass-english-speaking-api && pytest -v 2>&1 | tail -20
Expected: All tests PASS.
git add app/api/dialogue.py tests/api/test_dialogue_greeting.py
git commit -m "feat(speaking): wire grade/vocabulary/sentences through /session route"
TopicDiscussionConfig and SessionConfig typesRepo: /Users/buoy/Development/gitrepo/PPT
Files:
src/types/englishSpeaking.ts:51-57Modify: src/types/englishSpeaking.ts:240-246
[ ] Step 1: Add grade to TopicDiscussionConfig
In src/types/englishSpeaking.ts, replace the TopicDiscussionConfig interface (lines 51–57):
// 话题讨论配置
export interface TopicDiscussionConfig {
topic: string
grade: string // grade.id from curriculum.json (e.g., 'grade5-1')
selectedRole: string
learningGoals: LearningGoals
practice: PracticeSettings
evaluation: EvaluationSettings
}
grade to SessionConfigIn the same file, replace the SessionConfig interface (lines 240–246):
// 对话会话配置
export interface SessionConfig {
topic: string
grade: string
roleId: string
totalRounds: number
vocabulary?: string[]
sentences?: string[]
}
Run: cd /Users/buoy/Development/gitrepo/PPT && npx vue-tsc --noEmit 2>&1 | head -30
Expected: Errors at sites where TopicDiscussionConfig is constructed without grade (DEFAULT_CONFIG) and where SessionConfig is constructed without grade (TopicDiscussionPreview's createSession call). These are addressed in Tasks 6 and 8.
cd /Users/buoy/Development/gitrepo/PPT
git add src/types/englishSpeaking.ts
git commit -m "feat(speaking): add grade to TopicDiscussionConfig and SessionConfig types"
DEFAULT_CONFIG + updateGrade)Repo: /Users/buoy/Development/gitrepo/PPT
Files:
src/store/speaking.ts:10-42 (DEFAULT_CONFIG)Modify: src/store/speaking.ts:78-82 (actions, near updateTopic)
[ ] Step 1: Add grade to DEFAULT_CONFIG
In src/store/speaking.ts, the DEFAULT_CONFIG object — add grade: 'grade5-1' right after topic:
const DEFAULT_CONFIG: TopicDiscussionConfig = {
topic: 'How I get to school',
grade: 'grade5-1',
selectedRole: 'tom',
learningGoals: {
// …unchanged
updateGrade actionIn the actions block, immediately after the updateTopic action:
// 年级
updateGrade(grade: string) {
this.config.grade = grade
},
Run: cd /Users/buoy/Development/gitrepo/PPT && npx vue-tsc --noEmit 2>&1 | grep -E "speaking\.ts|englishSpeaking\.ts" | head -10
Expected: No errors from speaking.ts or englishSpeaking.ts. Errors elsewhere (TopicDiscussionPreview) are still expected.
git add src/store/speaking.ts
git commit -m "feat(speaking): persist grade in store, add updateGrade action"
SpeakingPanel selectedGrade to storeRepo: /Users/buoy/Development/gitrepo/PPT
Files:
Modify: src/views/Editor/EnglishSpeaking/SpeakingPanel.vue:67-110
[ ] Step 1: Replace local selectedGrade ref with store binding
In src/views/Editor/EnglishSpeaking/SpeakingPanel.vue, the <script setup> section currently has at lines 80–82:
const selectedTextbook = ref('shep')
const selectedGrade = ref('grade5-1')
const selectedUnit = ref('SHEP-5-上-Unit2')
The store is already imported (line 71: import { useSpeakingStore } from '@/store/speaking') and instantiated as speakingStore (line 106).
Move the useSpeakingStore call up (right after the const pageMode line so it's before our binding), and replace the three refs block with:
const speakingStore = useSpeakingStore()
// 课本筛选条件的默认值(上海英语·五年级上·Unit2),供开发/演示用
const selectedTextbook = ref('shep')
const selectedUnit = ref('SHEP-5-上-Unit2')
// selectedGrade 持久化到 store(用于发送到后端 createSession 渲染 system_prompt)
const selectedGrade = computed({
get: () => speakingStore.config.grade,
set: (val: string) => speakingStore.updateGrade(val),
})
Add computed to the Vue import at line 68:
import { ref, computed, watch } from 'vue'
Remove the now-duplicate const speakingStore = useSpeakingStore() from line 106 (the watch block can use the same instance).
<Layer1Home> v-bind/v-on still worksThe template binding :selectedGrade="selectedGrade" and @update:selectedGrade="selectedGrade = $event" will continue to work because computed with a setter is assignable. No template change needed.
Run: cd /Users/buoy/Development/gitrepo/PPT && npx vue-tsc --noEmit 2>&1 | grep "SpeakingPanel" | head -5
Expected: No errors from SpeakingPanel.vue.
Run: cd /Users/buoy/Development/gitrepo/PPT && npm run dev (or whatever dev command this project uses — check package.json if unsure).
Open the English Speaking panel. Change the grade in the dropdown. Open Vue devtools (or add a temporary console.log(speakingStore.config.grade) in the panel) and confirm store.config.grade updates.
Stop the dev server.
git add src/views/Editor/EnglishSpeaking/SpeakingPanel.vue
git commit -m "feat(speaking): bind SpeakingPanel grade selector to speakingStore"
TopicDiscussionPreview passes grade/sentences in createSessionRepo: /Users/buoy/Development/gitrepo/PPT
Files:
Modify: src/views/Editor/EnglishSpeaking/preview/TopicDiscussionPreview.vue:213-218
[ ] Step 1: Extend the createSession call
In TopicDiscussionPreview.vue, the startDialogue function currently calls:
const info = await api.createSession({
topic: speakingStore.config.topic || props.topic,
totalRounds: speakingStore.config.practice.rounds ?? props.totalRounds,
roleId: mockRole.id,
vocabulary: speakingStore.config.learningGoals.vocabulary,
})
Replace with:
const info = await api.createSession({
topic: speakingStore.config.topic || props.topic,
grade: speakingStore.config.grade,
totalRounds: speakingStore.config.practice.rounds ?? props.totalRounds,
roleId: mockRole.id,
vocabulary: speakingStore.config.learningGoals.vocabulary,
sentences: speakingStore.config.learningGoals.sentences,
})
Run: cd /Users/buoy/Development/gitrepo/PPT && npx vue-tsc --noEmit 2>&1 | grep "TopicDiscussionPreview" | head -5
Expected: No errors. (Earlier grade missing error from Task 5 is now resolved.)
git add src/views/Editor/EnglishSpeaking/preview/TopicDiscussionPreview.vue
git commit -m "feat(speaking): pass grade and sentences to createSession"
RealDialogueAPI.createSession sends new fields on the wireRepo: /Users/buoy/Development/gitrepo/PPT
Files:
Modify: src/views/Editor/EnglishSpeaking/services/llmService.ts:155-176
[ ] Step 1: Extend the request body
In src/views/Editor/EnglishSpeaking/services/llmService.ts, the RealDialogueAPI.createSession method currently has:
body: JSON.stringify({
topic: config.topic,
totalRounds: config.totalRounds,
roleId: config.roleId,
}),
Replace with:
body: JSON.stringify({
topic: config.topic,
grade: config.grade,
vocabulary: config.vocabulary ?? [],
sentences: config.sentences ?? [],
totalRounds: config.totalRounds,
roleId: config.roleId,
}),
Also fix the MockDialogueAPI.createSession if it uses the rename — actually it doesn't read these fields, no change needed there.
Run: cd /Users/buoy/Development/gitrepo/PPT && npx vue-tsc --noEmit 2>&1 | tail -20
Expected: No errors related to this change. (Pre-existing unrelated errors, if any, are out of scope.)
git add src/views/Editor/EnglishSpeaking/services/llmService.ts
git commit -m "feat(speaking): send grade/vocabulary/sentences to backend /session"
Both repos.
Run in a terminal (or background): cd /Users/buoy/Development/gitrepo/cococlass-english-speaking-api && uvicorn app.main:app --reload --port 8000 (use whatever startup command the backend uses — check pyproject.toml / README if unsure).
curl -i -X POST http://localhost:8000/api/speaking/dialogue/session \
-H 'Content-Type: application/json' \
-d '{
"topic": "How I get to school",
"grade": "grade5-1",
"vocabulary": ["by bus", "on foot"],
"sentences": ["How do you come to school?", "I come by bus."],
"totalRounds": 3
}'
Expected: HTTP 200, response body has sessionId.
Connect to the backend's database (the connection string is in app/config.py or env). Query the row:
SELECT system_prompt FROM dialogue_session ORDER BY id DESC LIMIT 1;
(Or use sqlite3 path/to/db.sqlite "SELECT system_prompt FROM dialogue_session ORDER BY id DESC LIMIT 1;" if SQLite.)
Expected: Output contains grade5-1, How I get to school, by bus, on foot, - How do you come to school?, and the three Grade-Specific Adaptation Rules sections.
curl -i -X POST http://localhost:8000/api/speaking/dialogue/session \
-H 'Content-Type: application/json' \
-d '{"topic": "X"}'
Expected: HTTP 422 (Pydantic validation, missing grade).
Run: cd /Users/buoy/Development/gitrepo/PPT && npm run dev
Open the editor, navigate to English Speaking → 话题讨论 config page. Change the grade in SpeakingPanel, edit vocabulary or sentences, click "开始对话". Watch the network tab: the POST /session request body should include grade, vocabulary, sentences matching the UI state.
In the backend DB (or via repeating Step 3), confirm the latest session row's system_prompt reflects what the UI just sent.
Continue the dialogue flow in the UI — the AI's first turn should match the Grade 4–6 tier rules (10–15 word sentences, mostly simple/compound). If wildly off-tier, that's a prompt-engineering issue, not an integration issue — note for follow-up but doesn't block this plan.
No commit for Task 10 itself (it's verification).
pytest -v)vue-tsc --noEmit reports no new errorscurl POST /session with {topic, grade, vocabulary, sentences} returns 200 and the persisted system_prompt contains the rendered templategrade returns 422