study-mate-agent 0.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- study_mate_agent/__init__.py +43 -0
- study_mate_agent/_codec.py +7 -0
- study_mate_agent/agents/__init__.py +10 -0
- study_mate_agent/agents/assistant.py +247 -0
- study_mate_agent/agents/study_week.py +108 -0
- study_mate_agent/agents/tutor.py +249 -0
- study_mate_agent/mappers/__init__.py +4 -0
- study_mate_agent/mappers/message_mapper.py +15 -0
- study_mate_agent/models/__init__.py +24 -0
- study_mate_agent/models/chunk.py +12 -0
- study_mate_agent/models/llm.py +29 -0
- study_mate_agent/models/material.py +10 -0
- study_mate_agent/models/message.py +61 -0
- study_mate_agent/models/stream.py +38 -0
- study_mate_agent/ports/__init__.py +12 -0
- study_mate_agent/ports/assistant.py +39 -0
- study_mate_agent/ports/embedder.py +15 -0
- study_mate_agent/ports/study_week.py +13 -0
- study_mate_agent/ports/tutor.py +41 -0
- study_mate_agent/prompts/__init__.py +8 -0
- study_mate_agent/prompts/assistant_prompt.py +56 -0
- study_mate_agent/prompts/tutor_prompt.py +44 -0
- study_mate_agent-0.1.2.dist-info/METADATA +111 -0
- study_mate_agent-0.1.2.dist-info/RECORD +26 -0
- study_mate_agent-0.1.2.dist-info/WHEEL +4 -0
- study_mate_agent-0.1.2.dist-info/licenses/LICENSE +22 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import uuid as _0x_uuid
|
|
2
|
+
import datetime as _0x_datetime
|
|
3
|
+
from typing import AsyncGenerator as _0x_AG, Optional as _0x_Opt
|
|
4
|
+
|
|
5
|
+
from agents import (
|
|
6
|
+
Agent as _0x_A,
|
|
7
|
+
function_tool as _0x_ft,
|
|
8
|
+
Runner as _0x_R,
|
|
9
|
+
trace as _0x_tr,
|
|
10
|
+
ModelSettings as _0x_MS,
|
|
11
|
+
RunResultStreaming as _0x_RRS,
|
|
12
|
+
RawResponsesStreamEvent as _0x_RRSE,
|
|
13
|
+
)
|
|
14
|
+
from openai.types.responses import (
|
|
15
|
+
EasyInputMessageParam as _0x_EIMP,
|
|
16
|
+
ResponseTextDeltaEvent as _0x_RTDE,
|
|
17
|
+
ResponseContentPartDoneEvent as _0x_RCPDE,
|
|
18
|
+
ResponseOutputText as _0x_ROT,
|
|
19
|
+
ResponseCreatedEvent as _0x_RCE,
|
|
20
|
+
Response as _0x_Resp,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from study_mate_agent._codec import _0xdec as _0x_d
|
|
24
|
+
from study_mate_agent.ports.tutor import TeachingTutorAgent
|
|
25
|
+
from study_mate_agent.ports.embedder import EmbedderClient
|
|
26
|
+
from study_mate_agent.models.message import Message
|
|
27
|
+
from study_mate_agent.models.chunk import EmbedChunkModel
|
|
28
|
+
from study_mate_agent.models.llm import LLMClientModel
|
|
29
|
+
from study_mate_agent.models.material import SearchMaterialModel
|
|
30
|
+
from study_mate_agent.models.stream import (
|
|
31
|
+
StreamCompletedEventModel,
|
|
32
|
+
StreamCreatedEventModel,
|
|
33
|
+
StreamDeltaEventModel,
|
|
34
|
+
)
|
|
35
|
+
from study_mate_agent.mappers.message_mapper import to_llm_messages as _0x_tlm
|
|
36
|
+
from study_mate_agent.prompts.tutor_prompt import get_tutor_system_prompt as _0x_gtsp
|
|
37
|
+
|
|
38
|
+
_0x_TTD = "\u0065\u004a\u0078\u0074\u006b\u0073\u0031\u0071\u0032\u0030\u0041\u0051\u0078\u002b\u0039\u0036\u0069\u0073\u0047\u006e\u0042\u004e\u0049\u002b\u0051\u0046\u002b\u006d\u0045\u0050\u0043\u0070\u0074\u0049\u0064\u0043\u0036\u0061\u0045\u0055\u0035\u0047\u0068\u004e\u005a\u0064\u006b\u006b\u0067\u006d\u0059\u0064\u0075\u0055\u0069\u004f\u0051\u006d\u0053\u0055\u0047\u0045\u0046\u0055\u0057\u0034\u006c\u0056\u006f\u006c\u004d\u0065\u005a\u0032\u0066\u0032\u0048\u0054\u0070\u006a\u0032\u0059\u006b\u0043\u004f\u0063\u0031\u0071\u0050\u006e\u002f\u007a\u0048\u0031\u0046\u0053\u0041\u0056\u0030\u0033\u0056\u004e\u0035\u0051\u0045\u006f\u0048\u0056\u004d\u0077\u0072\u0075\u0067\u0065\u0059\u0068\u0058\u006b\u0039\u0077\u0030\u0059\u0042\u005a\u0075\u0065\u0053\u004e\u0072\u0049\u0035\u0078\u006e\u004c\u0031\u0033\u006e\u0050\u0059\u0054\u0063\u004d\u004a\u006d\u0041\u0041\u0063\u002f\u0076\u0076\u0066\u0037\u006e\u007a\u0035\u002b\u002b\u0066\u0062\u0035\u0075\u0050\u002f\u0031\u005a\u0039\u0074\u006c\u0058\u0035\u0074\u0050\u0044\u006a\u0038\u0034\u0037\u0038\u0043\u0055\u0057\u0076\u0070\u0053\u004e\u004a\u0041\u0043\u0069\u0065\u0055\u006a\u004f\u004c\u0043\u0065\u0053\u0033\u004e\u0046\u0051\u0057\u0061\u0039\u0077\u0070\u0034\u0072\u0073\u0047\u0065\u0046\u0031\u0054\u0065\u0048\u006e\u0066\u0079\u0067\u0077\u004b\u0053\u0068\u0070\u004d\u004c\u0066\u0038\u0064\u004f\u006d\u0064\u0065\u004b\u0077\u0074\u006b\u0048\u004e\u0078\u0061\u0063\u0052\u0042\u0079\u0068\u0033\u0041\u0056\u0065\u002b\u006a\u0065\u0072\u006e\u0036\u0068\u0030\u0064\u0068\u0070\u0045\u0045\u0053\u0051\u0063\u0059\u006e\u0050\u004d\u0043\u006e\u006e\u0041\u0077\u0050\u0069\u0037\u0071\u007a\u0071\u0059\u0074\u004a\u006b\u0064\u006f\u0048\u004e\u0075\u004c\u0043\u004c\u0043\u006f\u004b\u0059\u0032\u0050\u0077\u004b\u0077\u0062\u0074\u006b\u0043\u0070\u0078\u0076\u0048\u0039\u0045\u0064\u0042\u0066\u0052\u0057\u006b\u0049\u0070\u0069\u0034\u0070\u0063\u0034\u0056\u0076\u0033\u0065\u0044\u0079\u0046\u006f\u006a\u0068\u0055\u0069\u0031\u0038\u004a\u0079\u007a\u0061\u0032\u0063\u0069\u0055\u004c\u0070\u0068\u004e\u007a\u0044\u0037\u0048\u0045\u0065\u0039\u006c\u0042\u0061\u0067\u006e\u0077\u0048\u0054\u0030\u0045\u004c\u0066\u0071\u0043\u0059\u0058\u0049\u0036\u0079\u0055\u006b\u006b\u0072\u002b\u0053\u0033\u005a\u0051\u0078\u0044\u0073\u004f\u006e\u0044\u0066\u0032\u0035\u0070\u0057\u006e\u0046\u004e\u0076\u0049\u005a\u0067\u0069\u0035\u0043\u0055\u0064\u0070\u0071\u0062\u006c\u0059\u0038\u006d\u006e\u0058\u004b\u004f\u0046\u0070\u0052\u0050\u0075\u006a\u0063\u0078\u0038\u0034\u0030\u0072\u0069\u0075\u0072\u0074\u0079\u0076\u006a\u0038\u0074\u0046\u004f\u0066\u0055\u0071\u0055\u0044\u0048\u0038\u0031\u0072\u0072\u0063\u0037\u0041\u0043\u0035\u002f\u0041\u0054\u0034\u006f\u0075\u0073\u0074\u0036\u0037\u004f\u0075\u0049\u0076\u0049\u0033\u0051\u0066\u004e\u0045\u0044\u0057\u006b\u0056\u0034\u0032\u0051\u0044\u0046\u0069\u0076\u0035\u0046\u004d\u006f\u004b\u006d\u0074\u0061\u006c\u0048\u006e\u0063\u005a\u0076\u0062\u0064\u0069\u0052\u0066\u004c\u0065\u0072\u004a\u004e\u0033\u0056\u007a\u0034\u0044\u0078\u0048\u0076\u0071\u004b\u0058\u0079\u0072\u0044\u0035\u0064\u0044\u0071\u0046\u004d\u0069\u0058\u0053\u0033\u0053\u0051\u0035\u0062\u004b\u0070\u0059\u0070\u0057\u0042\u0054\u0067\u006f\u0061\u005a\u0077\u004a\u0074\u0079\u0068\u006b\u006c\u0037\u0075\u0035\u0058\u005a\u0048\u0031\u0059\u002f\u004a\u0066\u0073\u0076\u0059\u006f\u007a\u0058\u0072\u002b\u006f\u0054\u004f\u0056\u0075\u0052\u002b\u0057\u002b\u0033\u004c\u0050\u004e\u002b\u0077\u0039\u0053\u0063\u0073\u004f\u0059"
|
|
39
|
+
_0x_TER = "\u0065\u004a\u0078\u0046\u006a\u0074\u0031\u0071\u0077\u006b\u0041\u0051\u0068\u0065\u0039\u0039\u0069\u006e\u006d\u0041\u0034\u0069\u0073\u0056\u0043\u0072\u006b\u0071\u004c\u0056\u0053\u0051\u0055\u006b\u0054\u0059\u0036\u006c\u0071\u0032\u0070\u0071\u0057\u0042\u005a\u006a\u0056\u004b\u0046\u0043\u0057\u004b\u0073\u0056\u0065\u0069\u0032\u007a\u0059\u0058\u0065\u0061\u004c\u004d\u0032\u0058\u0066\u006f\u004a\u0049\u0069\u0039\u0047\u0038\u0036\u0063\u006e\u0036\u002f\u0033\u0030\u0041\u0030\u0036\u0054\u0033\u0033\u0069\u0064\u0055\u0071\u0039\u0078\u0079\u0043\u0034\u0076\u0062\u0037\u0076\u0033\u0074\u0030\u0045\u006e\u0054\u0036\u0079\u0045\u006f\u0065\u0063\u0073\u0049\u0077\u0034\u0065\u002b\u004e\u0074\u0053\u0064\u0056\u0052\u0059\u0066\u006a\u0071\u004a\u0077\u005a\u0070\u0079\u0059\u004f\u0045\u004b\u0071\u0064\u0034\u006c\u0052\u004d\u0050\u0048\u004f\u005a\u0037\u004c\u0042\u0078\u0068\u002b\u006f\u004c\u0078\u004e\u0034\u0065\u0047\u0077\u0030\u0032\u0037\u0068\u0065\u0045\u007a\u0055\u006b\u0033\u0056\u0049\u0065\u0056\u0052\u0056\u0045\u0064\u006b\u0067\u0050\u0057\u006e\u0054\u0078\u0052\u0035\u004b\u002b\u0066\u0052\u0045\u004c\u002f\u0048\u0068\u0044\u0044\u006c\u0044\u002b\u0032\u0074\u0049\u007a\u0034\u0035\u0036\u0041\u004c\u007a\u0075\u0045\u0033\u0056\u0062\u0038\u0048\u004c\u0052\u0050\u0053\u0036\u0055\u0039\u0070\u0034\u0037\u0031\u0072\u004e\u0051\u0073\u004f\u0045\u0061\u0051\u0051\u0074\u0044\u0061\u004a\u006e\u004f\u0058\u0061\u004b\u0059\u004d\u0064\u0059\u0071\u0041\u0074\u004a\u0053\u0054\u0043\u004a\u0067\u004a\u0074\u002f\u006d\u0073\u0061\u0038\u004c\u0063\u0034\u0046\u0046\u0032\u0064\u0052\u0070\u002f\u0033\u004d\u0079\u0075\u0056\u0074\u0055\u0070\u0031\u0057\u0056\u0034\u0053\u0066\u0047\u004d\u0056\u0061\u0065\u004f\u006f\u004a\u0036\u0041\u0031\u002f\u006a\u0065\u0052\u0056\u002b\u0033\u0068\u006e\u007a\u006e\u0078\u002f\u0073\u006f\u0076\u0041\u0034\u0067\u003d\u003d"
|
|
40
|
+
_0x_TFR = "\u0065\u004a\u0077\u0056\u006a\u0055\u0045\u004b\u0077\u006a\u0041\u0055\u0052\u0050\u0063\u0039\u0078\u0054\u002b\u0042\u0042\u002f\u0041\u0079\u0067\u0070\u004b\u0046\u0053\u0043\u0076\u0057\u0046\u0070\u0045\u0051\u004b\u0044\u0052\u0043\u0068\u0053\u0077\u0055\u0044\u0046\u0052\u0070\u0052\u0061\u006d\u0043\u0075\u006f\u0071\u0031\u0059\u0049\u0058\u0069\u0067\u005a\u004b\u0066\u004f\u0078\u0067\u0058\u0077\u0079\u007a\u0065\u0038\u0049\u0062\u004f\u0059\u0068\u0049\u0075\u0047\u0064\u0041\u0046\u0049\u005a\u004e\u0042\u0045\u0050\u0074\u0044\u0045\u006a\u004b\u0073\u004f\u006c\u0051\u0033\u0077\u004f\u0050\u0047\u0056\u0041\u004a\u0030\u006e\u0057\u0043\u0036\u0064\u0071\u0058\u0030\u0071\u0077\u004d\u0036\u006d\u0073\u005a\u0042\u0078\u0050\u0053\u007a\u0036\u0058\u0074\u0030\u0048\u0042\u0046\u002f\u007a\u006a\u007a\u006b\u0046\u002f\u004e\u0059\u0057\u0058\u006b\u0043\u0049\u0077\u0056\u0067\u0077\u0066\u002f\u0042\u0054\u0077\u0035\u0032\u0057\u002b\u0068\u0057\u006d\u0057\u0073\u0048\u002b\u0074\u0030\u0061\u006b\u005a\u0068\u007a\u0041\u0056\u0069\u0032\u0065\u004c\u0068\u0062\u0032\u0059\u0043\u0056\u0065\u0030\u0078\u004c\u004c\u0042\u0074\u0041\u0039\u0055\u0058\u004a\u006a\u0064\u004e\u006a\u006c\u0072\u0076\u0066\u0054\u004e\u0066\u0043\u0034\u0052\u0079\u0035\u0032\u002b\u0035\u0036\u0050\u0077\u0071\u0058\u0059\u0059\u004d\u003d"
|
|
41
|
+
_0x_NO_EMB = "eJyqVrVSqlKjSUvUs7hKkfLLEjcUkexIR2WpVFKnu7iFiQGqJmvivfJ3elkRpF9VisceAhjrLzeTvdtTe2mvbDq2TRXtftP7W4oAK5eBgA=="
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TeachingTutorOpenAIAgent(TeachingTutorAgent):
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
agent_model: str = "gpt-6-luna",
|
|
49
|
+
embedder_client: _0x_Opt[EmbedderClient] = None,
|
|
50
|
+
max_searches: int = 5,
|
|
51
|
+
temperature: float = 0.0,
|
|
52
|
+
tool_choice: str = "required",
|
|
53
|
+
system_prompt_template: _0x_Opt[str] = None,
|
|
54
|
+
additional_rules: _0x_Opt[str] = None,
|
|
55
|
+
):
|
|
56
|
+
self.agent_model = agent_model
|
|
57
|
+
self.embedder_client = embedder_client
|
|
58
|
+
self.max_searches = max_searches
|
|
59
|
+
self.temperature = temperature
|
|
60
|
+
self.tool_choice = tool_choice
|
|
61
|
+
self.system_prompt_template = system_prompt_template
|
|
62
|
+
self.additional_rules = additional_rules
|
|
63
|
+
|
|
64
|
+
async def _search_course_materials(
|
|
65
|
+
self,
|
|
66
|
+
bucket_id: _0x_uuid.UUID,
|
|
67
|
+
query: str,
|
|
68
|
+
week_number: int,
|
|
69
|
+
) -> tuple[str, list[EmbedChunkModel]]:
|
|
70
|
+
if not self.embedder_client:
|
|
71
|
+
return _0x_d(_0x_NO_EMB), []
|
|
72
|
+
|
|
73
|
+
_0x_res: list[EmbedChunkModel] = await self.embedder_client.embed_hybrid_query(
|
|
74
|
+
bucket_id,
|
|
75
|
+
query,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if len(_0x_res) == 0:
|
|
79
|
+
_0x_mat = _0x_d(_0x_TER).format(
|
|
80
|
+
query=query,
|
|
81
|
+
week_number=week_number,
|
|
82
|
+
)
|
|
83
|
+
else:
|
|
84
|
+
_0x_lines = chr(10).join([f'{r.source_name} |{r.timeline}|{r.text}' for r in _0x_res])
|
|
85
|
+
_0x_mat = _0x_d(_0x_TFR).format(
|
|
86
|
+
query=query,
|
|
87
|
+
week_number=week_number,
|
|
88
|
+
count=len(_0x_res),
|
|
89
|
+
items=_0x_lines,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return _0x_mat, _0x_res
|
|
93
|
+
|
|
94
|
+
async def _create_agent(
|
|
95
|
+
self,
|
|
96
|
+
bucket_id: _0x_uuid.UUID,
|
|
97
|
+
week_number: int,
|
|
98
|
+
course_name: str,
|
|
99
|
+
week_topic: str,
|
|
100
|
+
embed_chunk_models: list[EmbedChunkModel],
|
|
101
|
+
max_searches: _0x_Opt[int] = None,
|
|
102
|
+
additional_rules: _0x_Opt[str] = None,
|
|
103
|
+
system_prompt_template: _0x_Opt[str] = None,
|
|
104
|
+
) -> _0x_A:
|
|
105
|
+
_0x_ms = max_searches if max_searches is not None else self.max_searches
|
|
106
|
+
_0x_ar = additional_rules if additional_rules is not None else self.additional_rules
|
|
107
|
+
_0x_tmpl = system_prompt_template if system_prompt_template is not None else self.system_prompt_template
|
|
108
|
+
|
|
109
|
+
@_0x_ft(
|
|
110
|
+
name_override="search_course_materials",
|
|
111
|
+
description_override=_0x_d(_0x_TTD).format(week_number=week_number),
|
|
112
|
+
)
|
|
113
|
+
async def search_course_materials(
|
|
114
|
+
query: str,
|
|
115
|
+
) -> str:
|
|
116
|
+
_0x_mat, _0x_res = await self._search_course_materials(
|
|
117
|
+
bucket_id=bucket_id,
|
|
118
|
+
query=query,
|
|
119
|
+
week_number=week_number,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
embed_chunk_models.clear()
|
|
123
|
+
embed_chunk_models.extend(_0x_res)
|
|
124
|
+
|
|
125
|
+
return _0x_mat
|
|
126
|
+
|
|
127
|
+
return _0x_A(
|
|
128
|
+
name="teaching tutor",
|
|
129
|
+
instructions=_0x_gtsp(
|
|
130
|
+
course_name=course_name,
|
|
131
|
+
week_number=week_number,
|
|
132
|
+
week_topic=week_topic,
|
|
133
|
+
max_searches=_0x_ms,
|
|
134
|
+
template=_0x_tmpl,
|
|
135
|
+
additional_rules=_0x_ar,
|
|
136
|
+
),
|
|
137
|
+
model_settings=_0x_MS(
|
|
138
|
+
tool_choice=self.tool_choice,
|
|
139
|
+
),
|
|
140
|
+
model=self.agent_model,
|
|
141
|
+
output_type=str,
|
|
142
|
+
tools=[search_course_materials],
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
async def create(
|
|
146
|
+
self,
|
|
147
|
+
bucket_id: _0x_uuid.UUID,
|
|
148
|
+
messages: list[Message],
|
|
149
|
+
course_name: str,
|
|
150
|
+
week_number: int,
|
|
151
|
+
week_topic: str,
|
|
152
|
+
max_turns: int = 5,
|
|
153
|
+
max_searches: _0x_Opt[int] = None,
|
|
154
|
+
additional_rules: _0x_Opt[str] = None,
|
|
155
|
+
system_prompt_template: _0x_Opt[str] = None,
|
|
156
|
+
) -> SearchMaterialModel:
|
|
157
|
+
_0x_ecm: list[EmbedChunkModel] = []
|
|
158
|
+
|
|
159
|
+
_0x_ag = await self._create_agent(
|
|
160
|
+
bucket_id=bucket_id,
|
|
161
|
+
week_number=week_number,
|
|
162
|
+
course_name=course_name,
|
|
163
|
+
week_topic=week_topic,
|
|
164
|
+
embed_chunk_models=_0x_ecm,
|
|
165
|
+
max_searches=max_searches,
|
|
166
|
+
additional_rules=additional_rules,
|
|
167
|
+
system_prompt_template=system_prompt_template,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
_0x_im: list[_0x_EIMP] = []
|
|
171
|
+
_0x_im.extend(_0x_tlm(messages))
|
|
172
|
+
|
|
173
|
+
with _0x_tr("teaching tutor"):
|
|
174
|
+
_0x_run = await _0x_R.run(_0x_ag, _0x_im, max_turns=max_turns)
|
|
175
|
+
_0x_out = _0x_run.final_output
|
|
176
|
+
|
|
177
|
+
_0x_llm = LLMClientModel(
|
|
178
|
+
text=_0x_out,
|
|
179
|
+
temperature=self.temperature,
|
|
180
|
+
model=self.agent_model,
|
|
181
|
+
input_tokens=0,
|
|
182
|
+
output_tokens=0,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
return SearchMaterialModel(_0x_llm, _0x_ecm)
|
|
186
|
+
|
|
187
|
+
async def create_stream(
|
|
188
|
+
self,
|
|
189
|
+
bucket_id: _0x_uuid.UUID,
|
|
190
|
+
messages: list[Message],
|
|
191
|
+
course_name: str,
|
|
192
|
+
week_number: int,
|
|
193
|
+
week_topic: str,
|
|
194
|
+
max_turns: int = 5,
|
|
195
|
+
max_searches: _0x_Opt[int] = None,
|
|
196
|
+
additional_rules: _0x_Opt[str] = None,
|
|
197
|
+
system_prompt_template: _0x_Opt[str] = None,
|
|
198
|
+
) -> _0x_AG[
|
|
199
|
+
StreamCompletedEventModel | StreamCreatedEventModel | StreamDeltaEventModel,
|
|
200
|
+
None,
|
|
201
|
+
]:
|
|
202
|
+
_0x_ecm: list[EmbedChunkModel] = []
|
|
203
|
+
|
|
204
|
+
_0x_ag = await self._create_agent(
|
|
205
|
+
bucket_id=bucket_id,
|
|
206
|
+
week_number=week_number,
|
|
207
|
+
course_name=course_name,
|
|
208
|
+
week_topic=week_topic,
|
|
209
|
+
embed_chunk_models=_0x_ecm,
|
|
210
|
+
max_searches=max_searches,
|
|
211
|
+
additional_rules=additional_rules,
|
|
212
|
+
system_prompt_template=system_prompt_template,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
_0x_im: list[_0x_EIMP] = []
|
|
216
|
+
_0x_im.extend(_0x_tlm(messages))
|
|
217
|
+
|
|
218
|
+
with _0x_tr("teaching tutor stream"):
|
|
219
|
+
_0x_rrs: _0x_RRS = _0x_R.run_streamed(_0x_ag, _0x_im, max_turns=max_turns)
|
|
220
|
+
_0x_ces = False
|
|
221
|
+
|
|
222
|
+
async for _0x_ev in _0x_rrs.stream_events():
|
|
223
|
+
if isinstance(_0x_ev, _0x_RRSE) and isinstance(_0x_ev.data, _0x_RTDE):
|
|
224
|
+
yield StreamDeltaEventModel(content=_0x_ev.data.delta)
|
|
225
|
+
|
|
226
|
+
elif (
|
|
227
|
+
isinstance(_0x_ev, _0x_RRSE)
|
|
228
|
+
and isinstance(_0x_ev.data, _0x_RCPDE)
|
|
229
|
+
and isinstance(_0x_ev.data.part, _0x_ROT)
|
|
230
|
+
):
|
|
231
|
+
yield StreamCompletedEventModel(
|
|
232
|
+
content=_0x_ev.data.part.text,
|
|
233
|
+
intput_tokens=0,
|
|
234
|
+
output_tokens=0,
|
|
235
|
+
sources=_0x_ecm,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
elif (
|
|
239
|
+
isinstance(_0x_ev, _0x_RRSE)
|
|
240
|
+
and isinstance(_0x_ev.data, _0x_RCE)
|
|
241
|
+
and isinstance(_0x_ev.data.response, _0x_Resp)
|
|
242
|
+
):
|
|
243
|
+
if not _0x_ces:
|
|
244
|
+
_0x_ces = True
|
|
245
|
+
yield StreamCreatedEventModel(
|
|
246
|
+
created_at=int(_0x_datetime.datetime.now().timestamp()),
|
|
247
|
+
temperature=self.temperature,
|
|
248
|
+
model=_0x_ev.data.response.model,
|
|
249
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from openai.types.responses import EasyInputMessageParam
|
|
2
|
+
from study_mate_agent.models.message import Message
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def to_llm_messages(messages: list[Message]) -> list[EasyInputMessageParam]:
|
|
6
|
+
llm_messages: list[EasyInputMessageParam] = []
|
|
7
|
+
|
|
8
|
+
for message in messages:
|
|
9
|
+
llm_messages.append({
|
|
10
|
+
"role": message.role.label,
|
|
11
|
+
"content": message.content,
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
return llm_messages
|
|
15
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from study_mate_agent.models.chunk import EmbedChunkModel
|
|
2
|
+
from study_mate_agent.models.llm import LLMClientModel
|
|
3
|
+
from study_mate_agent.models.material import SearchMaterialModel
|
|
4
|
+
from study_mate_agent.models.message import Message, MessageRole, MessageRoleLabel
|
|
5
|
+
from study_mate_agent.models.stream import (
|
|
6
|
+
StreamCreatedEventModel,
|
|
7
|
+
StreamDeltaEventModel,
|
|
8
|
+
StreamCompletedEventModel,
|
|
9
|
+
StreamRagEventModel,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"EmbedChunkModel",
|
|
14
|
+
"LLMClientModel",
|
|
15
|
+
"SearchMaterialModel",
|
|
16
|
+
"Message",
|
|
17
|
+
"MessageRole",
|
|
18
|
+
"MessageRoleLabel",
|
|
19
|
+
"StreamCreatedEventModel",
|
|
20
|
+
"StreamDeltaEventModel",
|
|
21
|
+
"StreamCompletedEventModel",
|
|
22
|
+
"StreamRagEventModel",
|
|
23
|
+
]
|
|
24
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
class LLMClientModel:
|
|
2
|
+
text: str
|
|
3
|
+
temperature: float
|
|
4
|
+
model: str
|
|
5
|
+
input_tokens: int
|
|
6
|
+
output_tokens: int
|
|
7
|
+
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
text: str,
|
|
11
|
+
temperature: float,
|
|
12
|
+
model: str,
|
|
13
|
+
input_tokens: int = 0,
|
|
14
|
+
output_tokens: int = 0,
|
|
15
|
+
):
|
|
16
|
+
self.text = text
|
|
17
|
+
self.temperature = temperature
|
|
18
|
+
self.model = model
|
|
19
|
+
self.input_tokens = input_tokens
|
|
20
|
+
self.output_tokens = output_tokens
|
|
21
|
+
|
|
22
|
+
def __repr__(self) -> str:
|
|
23
|
+
return (
|
|
24
|
+
f"LLMClientModel(model={self.model!r}, "
|
|
25
|
+
f"temperature={self.temperature}, "
|
|
26
|
+
f"input_tokens={self.input_tokens}, "
|
|
27
|
+
f"output_tokens={self.output_tokens})"
|
|
28
|
+
)
|
|
29
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from study_mate_agent.models.chunk import EmbedChunkModel
|
|
3
|
+
from study_mate_agent.models.llm import LLMClientModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class SearchMaterialModel:
|
|
8
|
+
llm_client_model: LLMClientModel
|
|
9
|
+
embed_chunk_models: list[EmbedChunkModel]
|
|
10
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
from typing import Literal, Optional, Any, cast
|
|
5
|
+
|
|
6
|
+
MessageRoleLabel = Literal["system", "user", "assistant"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MessageRole(StrEnum):
|
|
10
|
+
system = "system"
|
|
11
|
+
user = "user"
|
|
12
|
+
assistant = "assistant"
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def label(self) -> MessageRoleLabel:
|
|
16
|
+
return cast(MessageRoleLabel, self.value)
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def from_label(cls, label: MessageRoleLabel | str) -> "MessageRole":
|
|
20
|
+
return cls(str(label))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Message:
|
|
24
|
+
id_: Optional[int]
|
|
25
|
+
content: str
|
|
26
|
+
thread_id: Optional[uuid.UUID]
|
|
27
|
+
role: MessageRole
|
|
28
|
+
message_bucket: Optional[Any]
|
|
29
|
+
usage: Optional[Any]
|
|
30
|
+
reaction: Optional[Any]
|
|
31
|
+
created_at: datetime
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
content: str,
|
|
36
|
+
role: MessageRole | str = MessageRole.user,
|
|
37
|
+
id_: Optional[int] = None,
|
|
38
|
+
thread_id: Optional[uuid.UUID] = None,
|
|
39
|
+
message_bucket: Optional[Any] = None,
|
|
40
|
+
usage: Optional[Any] = None,
|
|
41
|
+
reaction: Optional[Any] = None,
|
|
42
|
+
created_at: Optional[datetime] = None,
|
|
43
|
+
):
|
|
44
|
+
self.id_ = id_
|
|
45
|
+
self.content = content
|
|
46
|
+
self.thread_id = thread_id
|
|
47
|
+
self.role = role if isinstance(role, MessageRole) else MessageRole.from_label(role)
|
|
48
|
+
self.message_bucket = message_bucket
|
|
49
|
+
self.usage = usage
|
|
50
|
+
self.reaction = reaction
|
|
51
|
+
self.created_at = created_at or datetime.now()
|
|
52
|
+
|
|
53
|
+
def __eq__(self, other: Any) -> bool:
|
|
54
|
+
return isinstance(other, Message) and other.id_ == self.id_ and self.id_ is not None
|
|
55
|
+
|
|
56
|
+
def __hash__(self) -> int:
|
|
57
|
+
return hash(self.id_) if self.id_ is not None else hash((self.content, self.role))
|
|
58
|
+
|
|
59
|
+
def __repr__(self) -> str:
|
|
60
|
+
return f"Message(id_={self.id_}, content={self.content!r}, thread_id={self.thread_id}, role={self.role})"
|
|
61
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from study_mate_agent.models.chunk import EmbedChunkModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class StreamCreatedEventModel:
|
|
8
|
+
created_at: int
|
|
9
|
+
model: str
|
|
10
|
+
temperature: float
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class StreamDeltaEventModel:
|
|
15
|
+
content: str
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class StreamCompletedEventModel:
|
|
20
|
+
content: str
|
|
21
|
+
intput_tokens: int = 0
|
|
22
|
+
output_tokens: int = 0
|
|
23
|
+
sources: Optional[list[EmbedChunkModel]] = field(default_factory=list)
|
|
24
|
+
id: Optional[int] = None
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def input_tokens(self) -> int:
|
|
28
|
+
return self.intput_tokens
|
|
29
|
+
|
|
30
|
+
@input_tokens.setter
|
|
31
|
+
def input_tokens(self, value: int) -> None:
|
|
32
|
+
self.intput_tokens = value
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class StreamRagEventModel:
|
|
37
|
+
sources: Optional[list[EmbedChunkModel]] = None
|
|
38
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from study_mate_agent.ports.assistant import LectureAssistantAgent
|
|
2
|
+
from study_mate_agent.ports.embedder import EmbedderClient
|
|
3
|
+
from study_mate_agent.ports.study_week import StudyWeekAgent
|
|
4
|
+
from study_mate_agent.ports.tutor import TeachingTutorAgent
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"LectureAssistantAgent",
|
|
8
|
+
"EmbedderClient",
|
|
9
|
+
"StudyWeekAgent",
|
|
10
|
+
"TeachingTutorAgent",
|
|
11
|
+
]
|
|
12
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import AsyncGenerator, Optional
|
|
4
|
+
from study_mate_agent.models.message import Message
|
|
5
|
+
from study_mate_agent.models.material import SearchMaterialModel
|
|
6
|
+
from study_mate_agent.models.stream import (
|
|
7
|
+
StreamCompletedEventModel,
|
|
8
|
+
StreamCreatedEventModel,
|
|
9
|
+
StreamDeltaEventModel,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LectureAssistantAgent(ABC):
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
async def create(
|
|
17
|
+
self,
|
|
18
|
+
bucket_id: uuid.UUID,
|
|
19
|
+
messages: list[Message],
|
|
20
|
+
week_number: int,
|
|
21
|
+
course_name: str,
|
|
22
|
+
max_turns: int = 5,
|
|
23
|
+
additional_rules: Optional[str] = None,
|
|
24
|
+
reference_date: Optional[str] = None,
|
|
25
|
+
) -> SearchMaterialModel:
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
async def create_stream(
|
|
30
|
+
self,
|
|
31
|
+
bucket_id: uuid.UUID,
|
|
32
|
+
messages: list[Message],
|
|
33
|
+
week_number: int,
|
|
34
|
+
course_name: str,
|
|
35
|
+
max_turns: int = 5,
|
|
36
|
+
additional_rules: Optional[str] = None,
|
|
37
|
+
reference_date: Optional[str] = None,
|
|
38
|
+
) -> AsyncGenerator[StreamCompletedEventModel | StreamCreatedEventModel | StreamDeltaEventModel, None]:
|
|
39
|
+
...
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from study_mate_agent.models.chunk import EmbedChunkModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class EmbedderClient(ABC):
|
|
7
|
+
|
|
8
|
+
@abstractmethod
|
|
9
|
+
async def embed_query(self, bucket_id: uuid.UUID, query: str) -> list[EmbedChunkModel]:
|
|
10
|
+
...
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
async def embed_hybrid_query(self, bucket_id: uuid.UUID, query: str) -> list[EmbedChunkModel]:
|
|
14
|
+
...
|
|
15
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import AsyncGenerator, Optional
|
|
4
|
+
from study_mate_agent.models.message import Message
|
|
5
|
+
from study_mate_agent.models.material import SearchMaterialModel
|
|
6
|
+
from study_mate_agent.models.stream import (
|
|
7
|
+
StreamCompletedEventModel,
|
|
8
|
+
StreamCreatedEventModel,
|
|
9
|
+
StreamDeltaEventModel,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TeachingTutorAgent(ABC):
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
async def create(
|
|
17
|
+
self,
|
|
18
|
+
bucket_id: uuid.UUID,
|
|
19
|
+
messages: list[Message],
|
|
20
|
+
course_name: str,
|
|
21
|
+
week_number: int,
|
|
22
|
+
week_topic: str,
|
|
23
|
+
max_turns: int = 5,
|
|
24
|
+
max_searches: Optional[int] = None,
|
|
25
|
+
additional_rules: Optional[str] = None,
|
|
26
|
+
) -> SearchMaterialModel:
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
async def create_stream(
|
|
31
|
+
self,
|
|
32
|
+
bucket_id: uuid.UUID,
|
|
33
|
+
messages: list[Message],
|
|
34
|
+
course_name: str,
|
|
35
|
+
week_number: int,
|
|
36
|
+
week_topic: str,
|
|
37
|
+
max_turns: int = 5,
|
|
38
|
+
max_searches: Optional[int] = None,
|
|
39
|
+
additional_rules: Optional[str] = None,
|
|
40
|
+
) -> AsyncGenerator[StreamCompletedEventModel | StreamCreatedEventModel | StreamDeltaEventModel, None]:
|
|
41
|
+
...
|