Skip to content

Commit f33d79b

Browse files
authored
Merge pull request #17 from lambda-feedback/dev
feat: add frontend workaround to avoid changing legacy code
2 parents ccdcb8d + 135a982 commit f33d79b

3 files changed

Lines changed: 110 additions & 5 deletions

File tree

evaluation_function/evaluation.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Any
22
from lf_toolkit.evaluation import Result as LFResult, Params
33

4-
from .schemas import FSA
4+
from .schemas import FSA, FSAFrontend
55
from .schemas.result import Result
66
from .correction import analyze_fsa_correction
77

@@ -16,17 +16,21 @@ def evaluation_function(
1616
Evaluate a student's FSA response against the expected answer.
1717
1818
Args:
19-
response: Student's FSA (dict with states, alphabet, transitions, etc.)
20-
answer: Expected FSA
19+
response: Student's FSA (dict with states, alphabet, transitions, etc.), since frontend constriants, this is FSAFrontend
20+
answer: Expected FSA still, FSAFrontend for the same reason
2121
params: Extra parameters (e.g., require_minimal)
2222
2323
Returns:
2424
LFResult with is_correct and feedback
2525
"""
2626
try:
2727
# Parse FSAs from input
28-
student_fsa = FSA.model_validate(response)
29-
expected_fsa = FSA.model_validate(answer)
28+
student_fsa_ = FSAFrontend.model_validate(response)
29+
expected_fsa_ = FSAFrontend.model_validate(answer)
30+
31+
student_fsa = student_fsa_.from_flattened()
32+
expected_fsa = expected_fsa_.from_flattened()
33+
3034

3135
# Get require_minimal from params if present
3236
require_minimal = params.get("require_minimal", False) if hasattr(params, "get") else False

evaluation_function/schemas/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .fsa import FSA, Transition
99
from .params import Params
1010
from .result import Result, ValidationError, ElementHighlight, FSAFeedback, ErrorCode, StructuralInfo
11+
from .fsaFrontend import FSAFrontend
1112

1213
__all__ = [
1314
# FSA representation
@@ -22,4 +23,5 @@
2223
"ErrorCode",
2324
"StructuralInfo",
2425
"FSAFeedback",
26+
"FSAFrontend"
2527
]
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
from typing import List
2+
from pydantic import BaseModel, Field
3+
from .fsa import FSA, Transition
4+
5+
# frontend zod restricts typing, this is the current workaround
6+
7+
class FSAFrontend(BaseModel):
8+
"""
9+
Finite State Automaton representation.
10+
11+
Represents a 5-tuple (Q, Σ, δ, q0, F) where:
12+
- Q = states
13+
- Σ = alphabet
14+
- δ = transitions (transition function)
15+
- q0 = initial_state
16+
- F = accept_states
17+
18+
Example:
19+
{
20+
"states": ["q0", "q1", "q2"],
21+
"alphabet": ["a", "b"],
22+
"transitions": [
23+
{"from_state": "q0", "to_state": "q1", "symbol": "a"},
24+
{"from_state": "q1", "to_state": "q2", "symbol": "b"}
25+
],
26+
"initial_state": "q0",
27+
"accept_states": ["q2"]
28+
}
29+
"""
30+
states: List[str] = Field(
31+
...,
32+
min_length=1,
33+
description="Q: Set of all state identifiers"
34+
)
35+
36+
alphabet: List[str] = Field(
37+
...,
38+
min_length=1,
39+
description="Σ: Input alphabet symbols (excluding epsilon)"
40+
)
41+
42+
transitions: List[str] = Field(
43+
default_factory=list,
44+
description="δ: Transition function as a list of (from_state, symbol, to_state) tuples"
45+
)
46+
47+
initial_state: str = Field(
48+
...,
49+
description="q0: The starting state"
50+
)
51+
52+
accept_states: List[str] = Field(
53+
default_factory=list,
54+
description="F: Set of accepting/final states"
55+
)
56+
57+
class Config:
58+
schema_extra = {
59+
"example": {
60+
"states": ["q0", "q1", "q2"],
61+
"alphabet": ["a", "b"],
62+
"transitions": [
63+
"q0|a|q1|",
64+
"q1|b|q2",
65+
],
66+
"initial_state": "q0",
67+
"accept_states": ["q2"]
68+
}
69+
}
70+
71+
@classmethod
72+
def from_flattened(cls, data: dict) -> FSA:
73+
"""
74+
Convert frontend FSA payload (with transitions as "from|symbol|to")
75+
into the FSABackend model with proper Transition objects.
76+
"""
77+
states = data.get("states", [])
78+
alphabet = data.get("alphabet", [])
79+
initial_state = data.get("initial_state", "q0")
80+
accept_states = data.get("accept_states", [])
81+
82+
flat_transitions = data.get("transitions", [])
83+
transitions: List[Transition] = []
84+
for t in flat_transitions:
85+
try:
86+
from_state, symbol, to_state = t.split("|")
87+
transitions.append(
88+
Transition(from_state=from_state, symbol=symbol, to_state=to_state)
89+
)
90+
except ValueError:
91+
raise ValueError(f"Invalid transition format: '{t}'")
92+
93+
return FSA(
94+
states=states,
95+
alphabet=alphabet,
96+
transitions=transitions,
97+
initial_state=initial_state,
98+
accept_states=accept_states,
99+
)

0 commit comments

Comments
 (0)