Skip to content

Commit 2483905

Browse files
author
= Enea_Gore
committed
refactor approaches
1 parent 95e8492 commit 2483905

File tree

17 files changed

+243
-203
lines changed

17 files changed

+243
-203
lines changed

llm_core/llm_core/utils/predict_and_parse.py

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ async def predict_and_parse(
1212
chat_prompt: ChatPromptTemplate,
1313
prompt_input: dict,
1414
pydantic_object: Type[T],
15-
tags: Optional[List[str]]
15+
tags: Optional[List[str]],
16+
use_function_calling: bool = False
1617
) -> Optional[T]:
1718
"""Predicts an LLM completion using the model and parses the output using the provided Pydantic model
1819
@@ -36,14 +37,30 @@ async def predict_and_parse(
3637
if experiment.run_id is not None:
3738
tags.append(f"run-{experiment.run_id}")
3839

39-
structured_output_llm = model.with_structured_output(pydantic_object)
40-
# chain = RunnableSequence(
41-
# chat_prompt,
42-
# structured_output_llm
43-
# )
44-
chain = chat_prompt | structured_output_llm
45-
46-
try:
47-
return await chain.ainvoke(prompt_input, config={"tags": tags}) # type: ignore #
48-
except ValidationError as e:
49-
raise ValueError(f"Could not parse output: {e}") from e
40+
41+
if (use_function_calling):
42+
structured_output_llm = model.with_structured_output(pydantic_object)
43+
chain = chat_prompt | structured_output_llm
44+
45+
try:
46+
result = await chain.ainvoke(prompt_input, config={"tags": tags})
47+
48+
if isinstance(result, pydantic_object):
49+
return result
50+
else:
51+
raise ValueError("Parsed output does not match the expected Pydantic model.")
52+
53+
except ValidationError as e:
54+
raise ValueError(f"Could not parse output: {e}") from e
55+
56+
else:
57+
structured_output_llm = model.with_structured_output(pydantic_object, method = "json_mode")
58+
chain = RunnableSequence(
59+
chat_prompt,
60+
structured_output_llm
61+
)
62+
try:
63+
return await chain.ainvoke(prompt_input, config={"tags": tags})
64+
except ValidationError as e:
65+
raise ValueError(f"Could not parse output: {e}") from e
66+

modules/text/module_text_llm/module_text_llm/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from module_text_llm.config import Configuration
1111
from module_text_llm.evaluation import get_feedback_statistics, get_llm_statistics
1212
from module_text_llm.generate_evaluation import generate_evaluation
13-
from module_text_llm.approaches.approach_controller import generate_suggestions
13+
from module_text_llm.approach_controller import generate_suggestions
1414

1515
@submissions_consumer
1616
def receive_submissions(exercise: Exercise, submissions: List[Submission]):

modules/text/module_text_llm/module_text_llm/approaches/approach_config.py renamed to modules/text/module_text_llm/module_text_llm/approach_config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ class ApproachType(str, Enum):
99

1010
class ApproachConfig(BaseModel, ABC):
1111
max_input_tokens: int = Field(default=3000, description="Maximum number of tokens in the input prompt.")
12-
model: ModelConfigType = Field(default=DefaultModelConfig()) # type: ignore
13-
type: ApproachType = Field(..., description="The type of approach config")
12+
model: ModelConfigType = Field(default=DefaultModelConfig())
13+
type: str = Field(..., description="The type of approach config")
1414

1515
class Config:
1616
use_enum_values = True
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
from typing import List
3+
from athena.text import Exercise, Submission, Feedback
4+
from module_text_llm.basic_approach import BasicApproachConfig
5+
from module_text_llm.chain_of_thought_approach import ChainOfThoughtConfig
6+
from module_text_llm.approach_config import ApproachConfig
7+
8+
from module_text_llm.basic_approach.generate_suggestions import generate_suggestions as generate_suggestions_basic
9+
from module_text_llm.chain_of_thought_approach.generate_suggestions import generate_suggestions as generate_cot_suggestions
10+
11+
async def generate_suggestions(exercise: Exercise, submission: Submission, config: ApproachConfig, debug: bool) -> List[Feedback]:
12+
if(isinstance(config, BasicApproachConfig)):
13+
return await generate_suggestions_basic(exercise, submission, config, debug)
14+
elif(isinstance(config, ChainOfThoughtConfig)):
15+
return await generate_cot_suggestions(exercise, submission, config, debug)
16+

modules/text/module_text_llm/module_text_llm/approaches/approach_controller.py

Lines changed: 0 additions & 18 deletions
This file was deleted.

modules/text/module_text_llm/module_text_llm/approaches/basic_approach/config.py

Lines changed: 0 additions & 26 deletions
This file was deleted.

modules/text/module_text_llm/module_text_llm/approaches/basic_approach/prompts/generate_suggestions.py

Lines changed: 0 additions & 31 deletions
This file was deleted.

modules/text/module_text_llm/module_text_llm/approaches/chain_of_thought_approach/config.py

Lines changed: 0 additions & 36 deletions
This file was deleted.

modules/text/module_text_llm/module_text_llm/approaches/chain_of_thought_approach/prompts/refined_cot_suggestions.py

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from module_text_llm.approach_config import ApproachConfig
2+
from pydantic import Field
3+
from typing import Literal
4+
5+
6+
from module_text_llm.basic_approach.prompt_generate_suggestions import GenerateSuggestionsPrompt
7+
8+
class BasicApproachConfig(ApproachConfig):
9+
type: Literal['basic'] = 'basic'
10+
generate_suggestions_prompt: GenerateSuggestionsPrompt = Field(default=GenerateSuggestionsPrompt())
11+
Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
from typing import List, Optional, Sequence
2-
from pydantic import BaseModel, Field
1+
from typing import List
32

43
from athena import emit_meta
54
from athena.text import Exercise, Submission, Feedback
@@ -9,29 +8,15 @@
98
check_prompt_length_and_omit_features_if_necessary,
109
num_tokens_from_prompt,
1110
)
11+
from athena.text import Exercise, Submission, Feedback
1212
from llm_core.utils.predict_and_parse import predict_and_parse
13+
1314
from module_text_llm.config import BasicApproachConfig
1415
from module_text_llm.helpers.utils import add_sentence_numbers, get_index_range_from_line_range, format_grading_instructions
15-
16-
class FeedbackModel(BaseModel):
17-
title: str = Field(description="Very short title, i.e. feedback category or similar", example="Logic Error")
18-
description: str = Field(description="Feedback description")
19-
line_start: Optional[int] = Field(description="Referenced line number start, or empty if unreferenced")
20-
line_end: Optional[int] = Field(description="Referenced line number end, or empty if unreferenced")
21-
credits: float = Field(0.0, description="Number of points received/deducted")
22-
grading_instruction_id: Optional[int] = Field(
23-
description="ID of the grading instruction that was used to generate this feedback, or empty if no grading instruction was used"
24-
)
25-
26-
27-
class AssessmentModel(BaseModel):
28-
"""Collection of feedbacks making up an assessment"""
29-
30-
feedbacks: List[FeedbackModel] = Field(description="Assessment feedbacks")
16+
from module_text_llm.basic_approach.prompt_generate_suggestions import AssessmentModel
3117

3218
async def generate_suggestions(exercise: Exercise, submission: Submission, config: BasicApproachConfig, debug: bool) -> List[Feedback]:
3319
model = config.model.get_model() # type: ignore[attr-defined]
34-
3520
prompt_input = {
3621
"max_points": exercise.max_points,
3722
"bonus_points": exercise.bonus_points,
@@ -74,7 +59,8 @@ async def generate_suggestions(exercise: Exercise, submission: Submission, confi
7459
tags=[
7560
f"exercise-{exercise.id}",
7661
f"submission-{submission.id}",
77-
]
62+
],
63+
use_function_calling=True
7864
)
7965

8066
if debug:
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from pydantic import Field, BaseModel
2+
from typing import List, Optional
3+
from pydantic import BaseModel, Field
4+
5+
system_message = """\
6+
You are an AI tutor for text assessment at a prestigious university.
7+
8+
# Task
9+
Create graded feedback suggestions for a student\'s text submission that a human tutor would accept. \
10+
Meaning, the feedback you provide should be applicable to the submission with little to no modification.
11+
12+
# Style
13+
1. Constructive, 2. Specific, 3. Balanced, 4. Clear and Concise, 5. Actionable, 6. Educational, 7. Contextual
14+
15+
# Problem statement
16+
{problem_statement}
17+
18+
# Example solution
19+
{example_solution}
20+
21+
# Grading instructions
22+
{grading_instructions}
23+
Max points: {max_points}, bonus points: {bonus_points}\
24+
25+
Respond in json.
26+
"""
27+
28+
human_message = """\
29+
Student\'s submission to grade (with sentence numbers <number>: <sentence>):
30+
31+
Respond in json.
32+
33+
\"\"\"
34+
{submission}
35+
\"\"\"\
36+
"""
37+
38+
# Input Prompt
39+
class GenerateSuggestionsPrompt(BaseModel):
40+
"""\
41+
Features available: **{problem_statement}**, **{example_solution}**, **{grading_instructions}**, **{max_points}**, **{bonus_points}**, **{submission}**
42+
43+
_Note: **{problem_statement}**, **{example_solution}**, or **{grading_instructions}** might be omitted if the input is too long._\
44+
"""
45+
system_message: str = Field(default=system_message,
46+
description="Message for priming AI behavior and instructing it what to do.")
47+
human_message: str = Field(default=human_message,
48+
description="Message from a human. The input on which the AI is supposed to act.")
49+
# Output Object
50+
class FeedbackModel(BaseModel):
51+
title: str = Field(description="Very short title, i.e. feedback category or similar", example="Logic Error")
52+
description: str = Field(description="Feedback description")
53+
line_start: Optional[int] = Field(description="Referenced line number start, or empty if unreferenced")
54+
line_end: Optional[int] = Field(description="Referenced line number end, or empty if unreferenced")
55+
credits: float = Field(0.0, description="Number of points received/deducted")
56+
grading_instruction_id: Optional[int] = Field(
57+
description="ID of the grading instruction that was used to generate this feedback, or empty if no grading instruction was used"
58+
)
59+
60+
61+
class AssessmentModel(BaseModel):
62+
"""Collection of feedbacks making up an assessment"""
63+
64+
feedbacks: List[FeedbackModel] = Field(description="Assessment feedbacks")
65+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from pydantic import BaseModel, Field
2+
from typing import Literal
3+
from llm_core.models import ModelConfigType, MiniModelConfig
4+
5+
from module_text_llm.approach_config import ApproachConfig
6+
from module_text_llm.chain_of_thought_approach.prompt_generate_feedback import CoTGenerateSuggestionsPrompt
7+
from module_text_llm.chain_of_thought_approach.prompt_thinking import ThinkingPrompt
8+
9+
class ChainOfThoughtConfig(ApproachConfig):
10+
# Defaults to the cheaper mini 4o model
11+
type: Literal['chain_of_thought'] = 'chain_of_thought'
12+
model: ModelConfigType = Field(default=MiniModelConfig) # type: ignore
13+
thikning_prompt: ThinkingPrompt = Field(default=ThinkingPrompt())
14+
generate_suggestions_prompt: CoTGenerateSuggestionsPrompt = Field(default=CoTGenerateSuggestionsPrompt())
15+

0 commit comments

Comments
 (0)