Architect-prompti parannettu, relaatiotuki templateihin, englanti-sääntö

- SPEC_SYSTEM: chain-of-thought, domain-esimerkit, anti-patternit, relaatiosäännöt
- Speksi-puhdistus: korjaa sa_type | None -virheet automaattisesti
- Etusivun teksti päivitetty
- Koodissa käytetään aina englantia (entity/field names)
This commit is contained in:
Jaakko Vanhala
2026-04-12 20:15:22 +03:00
parent 6ef71b7e5c
commit 8468724a4c
2 changed files with 38 additions and 30 deletions

View File

@@ -1121,33 +1121,38 @@ OUTPUT FORMAT:
// === Template Pipeline — rakennuspalaset ===
const SPEC_SYSTEM = `You are a software architect. Given a project description, output a JSON specification.
Output ONLY valid JSON, no explanations. Follow this exact schema:
{
"project_name": "short-name",
"description": "One sentence",
"entities": [
{
"name": "Todo",
"table_name": "todos",
"fields": [
{"name": "title", "sa_type": "String(255)", "py_type": "str", "nullable": false, "default": null},
{"name": "description", "sa_type": "Text", "py_type": "str | None", "nullable": true, "default": null},
{"name": "status", "sa_type": "String(20)", "py_type": "str", "nullable": false, "default": "pending"}
]
}
],
"extra_imports": ["from datetime import date"]
}
RULES:
- sa_type: SQLAlchemy column type (String(N), Text, Integer, Date, DateTime, Boolean, Float)
- py_type: Python type hint (str, int, float, bool, date, datetime, str | None, etc.)
- Do NOT use Enum — use String(20) with a default value for status fields
- nullable: true = optional field
- default: null = no default, otherwise a string/number value
- extra_imports: stdlib imports needed in schemas.py (e.g. "from datetime import date")
- entity name: PascalCase singular, table_name: snake_case plural
- Keep it simple: 1-3 entities, 3-7 fields each`;
const SPEC_SYSTEM = `You are a software architect who designs database schemas for Python web applications.
THINK STEP BY STEP before outputting JSON:
1. What are the main ENTITIES (nouns) in this project?
2. What FIELDS does each entity need? (name, type, required?)
3. Which entities REFERENCE each other? (e.g. "a Book belongs to an Author" → Book has author_id)
4. Are there Date/DateTime fields? → add extra_imports
Then output ONLY valid JSON (no explanations before or after).
SCHEMA:
{"project_name":"short-name","description":"One sentence","entities":[{"name":"EntityName","table_name":"entity_names","fields":[{"name":"field_name","sa_type":"String(255)","py_type":"str","nullable":false,"default":null}]}],"relationships":[{"from":"ChildEntity","field":"parent_id","to":"ParentEntity","type":"many-to-one"}],"extra_imports":[]}
FIELD RULES:
- sa_type: String(N), Text, Integer, Date, DateTime, Boolean, Float
- py_type: str, int, float, bool, date, datetime — append " | None" if nullable
- Status fields: use String(20) with default value, NEVER Enum
- Every entity gets "id" automatically — do NOT add id or redundant ID fields
- Use snake_case for field names
RELATIONSHIP RULES:
- If entity A "belongs to" entity B → A has b_id field (Integer, nullable=false) + relationship entry
- EVERY _id field MUST have a matching relationship entry
- Parent entities must appear BEFORE children in the entities array
- If no relationships, set "relationships": []
AVOID: redundant ID fields, generic names, more than 7 fields or 3 entities, non-English entity/field names (ALWAYS English even if description is Finnish)
EXAMPLES (adapt, don't copy):
Todo app → Todo: title(str), description(Text|None), due_date(Date|None), status(String20="pending")
E-commerce → Customer: name,email / Product: name,price(Float),stock(Int) / Order: customer_id→Customer, product_id→Product, quantity(Int), order_date(Date)
Blog → Author: name,email,bio(Text|None) / Post: title, content(Text), author_id→Author, published_at(DateTime|None), status(String20="draft")`;
function extractJson(text) {
const m = text.match(/```(?:json)?\s*\n([\s\S]*?)```/);

View File

@@ -17,17 +17,20 @@ body {
font-size: 16px;
background: var(--bg);
color: var(--text);
height: 100vh;
overflow: hidden;
min-height: 100vh;
}
.container {
max-width: 1600px;
margin: 0 auto;
padding: 20px 40px;
}
#app.container {
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
#app:not(.active) { display: none; }
@@ -44,7 +47,7 @@ body {
/* Panels */
.panel { display: none; }
.panel.active { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.panel.active { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow-y: auto; }
/* Status bar */
.status-bar {