process-gpt-agent-sdk 0.2.10__py3-none-any.whl → 0.3.10__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.
Potentially problematic release.
This version of process-gpt-agent-sdk might be problematic. Click here for more details.
- process_gpt_agent_sdk-0.3.10.dist-info/METADATA +336 -0
- process_gpt_agent_sdk-0.3.10.dist-info/RECORD +5 -0
- processgpt_agent_sdk/processgpt_agent_framework.py +402 -0
- process_gpt_agent_sdk-0.2.10.dist-info/METADATA +0 -1026
- process_gpt_agent_sdk-0.2.10.dist-info/RECORD +0 -19
- processgpt_agent_sdk/__init__.py +0 -11
- processgpt_agent_sdk/core/__init__.py +0 -0
- processgpt_agent_sdk/core/database.py +0 -464
- processgpt_agent_sdk/server.py +0 -313
- processgpt_agent_sdk/simulator.py +0 -231
- processgpt_agent_sdk/tools/__init__.py +0 -0
- processgpt_agent_sdk/tools/human_query_tool.py +0 -211
- processgpt_agent_sdk/tools/knowledge_tools.py +0 -206
- processgpt_agent_sdk/tools/safe_tool_loader.py +0 -209
- processgpt_agent_sdk/utils/__init__.py +0 -0
- processgpt_agent_sdk/utils/context_manager.py +0 -45
- processgpt_agent_sdk/utils/crewai_event_listener.py +0 -205
- processgpt_agent_sdk/utils/event_handler.py +0 -72
- processgpt_agent_sdk/utils/logger.py +0 -97
- processgpt_agent_sdk/utils/summarizer.py +0 -146
- {process_gpt_agent_sdk-0.2.10.dist-info → process_gpt_agent_sdk-0.3.10.dist-info}/WHEEL +0 -0
- {process_gpt_agent_sdk-0.2.10.dist-info → process_gpt_agent_sdk-0.3.10.dist-info}/top_level.txt +0 -0
processgpt_agent_sdk/server.py
DELETED
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from typing import Any, Dict
|
|
3
|
-
|
|
4
|
-
from a2a.server.agent_execution import AgentExecutor, RequestContext
|
|
5
|
-
from a2a.server.events import EventQueue, Event
|
|
6
|
-
|
|
7
|
-
from .core.database import (
|
|
8
|
-
fetch_human_users_by_proc_inst_id,
|
|
9
|
-
initialize_db,
|
|
10
|
-
get_consumer_id,
|
|
11
|
-
polling_pending_todos,
|
|
12
|
-
fetch_done_data,
|
|
13
|
-
fetch_agent_data,
|
|
14
|
-
fetch_form_types,
|
|
15
|
-
fetch_task_status,
|
|
16
|
-
fetch_tenant_mcp_config,
|
|
17
|
-
update_task_error,
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
from .utils.logger import handle_application_error, write_log_message, write_debug_message, write_info_message, DEBUG_LEVEL_BASIC, DEBUG_LEVEL_DETAILED, DEBUG_LEVEL_VERBOSE
|
|
21
|
-
from .utils.summarizer import summarize_async
|
|
22
|
-
from .utils.event_handler import process_event_message
|
|
23
|
-
from .utils.context_manager import set_context, reset_context
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
# =============================================================================
|
|
27
|
-
# 서버: ProcessGPTAgentServer
|
|
28
|
-
# 설명: 작업 폴링→실행 준비→실행/이벤트 저장→취소 감시까지 담당하는 핵심 서버
|
|
29
|
-
# =============================================================================
|
|
30
|
-
class ProcessGPTAgentServer:
|
|
31
|
-
"""ProcessGPT 핵심 서버
|
|
32
|
-
|
|
33
|
-
- 단일 실행기 모델: 실행기는 하나이며, 작업별 분기는 실행기 내부 로직에 위임합니다.
|
|
34
|
-
- 폴링은 타입 필터 없이(빈 값) 가져온 뒤, 작업 레코드의 정보로 처리합니다.
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
def __init__(self, executor: AgentExecutor, polling_interval: int = 5, agent_orch: str = ""):
|
|
38
|
-
"""서버 실행기/폴링 주기/오케스트레이션 값을 초기화한다."""
|
|
39
|
-
self.polling_interval = polling_interval
|
|
40
|
-
self.is_running = False
|
|
41
|
-
self._executor: AgentExecutor = executor
|
|
42
|
-
self.cancel_check_interval: float = 0.5
|
|
43
|
-
self.agent_orch: str = agent_orch or ""
|
|
44
|
-
initialize_db()
|
|
45
|
-
|
|
46
|
-
async def run(self) -> None:
|
|
47
|
-
"""메인 폴링 루프를 실행한다. 작업을 가져와 준비/실행/감시를 순차 수행."""
|
|
48
|
-
self.is_running = True
|
|
49
|
-
write_log_message("ProcessGPT 서버 시작")
|
|
50
|
-
write_debug_message(f"[DEBUG-001] 서버 초기화 완료 - polling_interval={self.polling_interval}s, agent_orch='{self.agent_orch}', cancel_check_interval={self.cancel_check_interval}s", DEBUG_LEVEL_BASIC)
|
|
51
|
-
|
|
52
|
-
while self.is_running:
|
|
53
|
-
try:
|
|
54
|
-
write_debug_message(f"[DEBUG-002] 폴링 시작 - agent_orch='{self.agent_orch}', consumer_id={get_consumer_id()}", DEBUG_LEVEL_VERBOSE)
|
|
55
|
-
task_record = await polling_pending_todos(self.agent_orch, get_consumer_id())
|
|
56
|
-
if not task_record:
|
|
57
|
-
write_debug_message(f"[DEBUG-003] 대기 중인 작업 없음 - {self.polling_interval}초 후 재시도", DEBUG_LEVEL_VERBOSE)
|
|
58
|
-
await asyncio.sleep(self.polling_interval)
|
|
59
|
-
continue
|
|
60
|
-
|
|
61
|
-
task_id = task_record["id"]
|
|
62
|
-
write_log_message(f"[JOB START] task_id={task_id}")
|
|
63
|
-
write_debug_message(f"[DEBUG-004] 작업 레코드 수신 - task_id={task_id}, proc_inst_id={task_record.get('proc_inst_id')}, user_id={task_record.get('user_id')}, tenant_id={task_record.get('tenant_id')}, activity_name={task_record.get('activity_name')}", DEBUG_LEVEL_BASIC)
|
|
64
|
-
|
|
65
|
-
try:
|
|
66
|
-
write_debug_message(f"[DEBUG-005] 서비스 데이터 준비 시작 - task_id={task_id}", DEBUG_LEVEL_DETAILED)
|
|
67
|
-
prepared_data = await self._prepare_service_data(task_record)
|
|
68
|
-
write_log_message(f"[RUN] 서비스 데이터 준비 완료 [task_id={task_id} agent={prepared_data.get('agent_orch','')}]")
|
|
69
|
-
write_debug_message(f"[DEBUG-006] 준비된 데이터 요약 - agent_list_count={len(prepared_data.get('agent_list', []))}, form_types_count={len(prepared_data.get('form_types', []))}, done_outputs_count={len(prepared_data.get('done_outputs', []))}, all_users_count={len(prepared_data.get('all_users', []))}", DEBUG_LEVEL_DETAILED)
|
|
70
|
-
|
|
71
|
-
write_debug_message(f"[DEBUG-007] 실행 및 취소 감시 시작 - task_id={task_id}", DEBUG_LEVEL_BASIC)
|
|
72
|
-
await self._execute_with_cancel_watch(task_record, prepared_data)
|
|
73
|
-
write_log_message(f"[RUN] 서비스 실행 완료 [task_id={task_id} agent={prepared_data.get('agent_orch','')}]")
|
|
74
|
-
write_debug_message(f"[DEBUG-008] 작업 완료 처리 - task_id={task_id}", DEBUG_LEVEL_BASIC)
|
|
75
|
-
except Exception as job_err:
|
|
76
|
-
write_debug_message(f"[DEBUG-009] 작업 처리 중 예외 발생 - task_id={task_id}, error_type={type(job_err).__name__}, error_message={str(job_err)}", DEBUG_LEVEL_BASIC)
|
|
77
|
-
handle_application_error("작업 처리 오류", job_err, raise_error=False)
|
|
78
|
-
try:
|
|
79
|
-
await update_task_error(str(task_id))
|
|
80
|
-
except Exception as upd_err:
|
|
81
|
-
handle_application_error("FAILED 상태 업데이트 실패", upd_err, raise_error=False)
|
|
82
|
-
continue
|
|
83
|
-
|
|
84
|
-
except Exception as e:
|
|
85
|
-
handle_application_error("폴링 루프 오류", e, raise_error=False)
|
|
86
|
-
await asyncio.sleep(self.polling_interval)
|
|
87
|
-
|
|
88
|
-
def stop(self) -> None:
|
|
89
|
-
"""폴링 루프를 중지 플래그로 멈춘다."""
|
|
90
|
-
self.is_running = False
|
|
91
|
-
write_log_message("ProcessGPT 서버 중지")
|
|
92
|
-
|
|
93
|
-
async def _prepare_service_data(self, task_record: Dict[str, Any]) -> Dict[str, Any]:
|
|
94
|
-
"""실행에 필요한 데이터(에이전트/폼/요약/사용자)를 준비해 dict로 반환."""
|
|
95
|
-
done_outputs = await fetch_done_data(task_record.get("proc_inst_id"))
|
|
96
|
-
write_log_message(f"[PREP] done_outputs → {done_outputs}")
|
|
97
|
-
feedbacks = task_record.get("feedback")
|
|
98
|
-
|
|
99
|
-
agent_list = await fetch_agent_data(str(task_record.get("user_id", "")))
|
|
100
|
-
write_log_message(f"[PREP] agent_list → {agent_list}")
|
|
101
|
-
|
|
102
|
-
mcp_config = await fetch_tenant_mcp_config(str(task_record.get("tenant_id", "")))
|
|
103
|
-
write_log_message(f"[PREP] mcp_config(툴) → {mcp_config}")
|
|
104
|
-
|
|
105
|
-
form_id, form_types, form_html = await fetch_form_types(
|
|
106
|
-
str(task_record.get("tool", "")),
|
|
107
|
-
str(task_record.get("tenant_id", ""))
|
|
108
|
-
)
|
|
109
|
-
write_log_message(f"[PREP] form → id={form_id} types={form_types}")
|
|
110
|
-
|
|
111
|
-
output_summary, feedback_summary = await summarize_async(
|
|
112
|
-
done_outputs or [], feedbacks or "", task_record.get("description", "")
|
|
113
|
-
)
|
|
114
|
-
write_log_message(f"[PREP] summary → output={output_summary} feedback={feedback_summary}")
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
all_users = await fetch_human_users_by_proc_inst_id(task_record.get("proc_inst_id"))
|
|
118
|
-
write_log_message(f"[PREP] all_users → {all_users}")
|
|
119
|
-
|
|
120
|
-
prepared: Dict[str, Any] = {
|
|
121
|
-
"task_id": str(task_record.get("id")),
|
|
122
|
-
"proc_inst_id": task_record.get("proc_inst_id"),
|
|
123
|
-
"agent_list": agent_list or [],
|
|
124
|
-
"mcp_config": mcp_config,
|
|
125
|
-
"form_id": form_id,
|
|
126
|
-
"todo_id": str(task_record.get("id")),
|
|
127
|
-
"form_types": form_types or [],
|
|
128
|
-
"form_html": form_html or "",
|
|
129
|
-
"activity_name": str(task_record.get("activity_name", "")),
|
|
130
|
-
"description": str(task_record.get("description", "")),
|
|
131
|
-
"agent_orch": str(task_record.get("agent_orch", "")),
|
|
132
|
-
"done_outputs": done_outputs or [],
|
|
133
|
-
"output_summary": output_summary or "",
|
|
134
|
-
"feedback_summary": feedback_summary or "",
|
|
135
|
-
"all_users": all_users or "",
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return prepared
|
|
139
|
-
|
|
140
|
-
async def _execute_with_cancel_watch(self, task_record: Dict[str, Any], prepared_data: Dict[str, Any]) -> None:
|
|
141
|
-
"""실행 태스크와 취소 감시 태스크를 동시에 운영한다."""
|
|
142
|
-
executor = self._executor
|
|
143
|
-
|
|
144
|
-
context = ProcessGPTRequestContext(prepared_data)
|
|
145
|
-
loop = asyncio.get_running_loop()
|
|
146
|
-
event_queue = ProcessGPTEventQueue(task_record, loop=loop)
|
|
147
|
-
|
|
148
|
-
try:
|
|
149
|
-
set_context(
|
|
150
|
-
todo_id=str(task_record.get("id")),
|
|
151
|
-
proc_inst_id=str(task_record.get("proc_inst_id") or ""),
|
|
152
|
-
crew_type=str(prepared_data.get("agent_orch") or ""),
|
|
153
|
-
form_id=str(prepared_data.get("form_id") or ""),
|
|
154
|
-
all_users=str(prepared_data.get("all_users") or ""),
|
|
155
|
-
)
|
|
156
|
-
except Exception as e:
|
|
157
|
-
handle_application_error("컨텍스트 설정 실패", e, raise_error=False)
|
|
158
|
-
|
|
159
|
-
write_log_message(f"[EXEC START] task_id={task_record.get('id')} agent={prepared_data.get('agent_orch','')}")
|
|
160
|
-
write_debug_message(f"[DEBUG-010] 실행기 및 이벤트 큐 초기화 완료 - task_id={task_record.get('id')}, context_data_keys={list(context.get_context_data().keys())}, event_queue_type={type(event_queue).__name__}", DEBUG_LEVEL_DETAILED)
|
|
161
|
-
execute_task = asyncio.create_task(executor.execute(context, event_queue))
|
|
162
|
-
cancel_watch_task = asyncio.create_task(self._watch_cancellation(task_record, executor, context, event_queue, execute_task))
|
|
163
|
-
write_debug_message(f"[DEBUG-011] 비동기 태스크 생성 완료 - execute_task={execute_task.get_name()}, cancel_watch_task={cancel_watch_task.get_name()}", DEBUG_LEVEL_DETAILED)
|
|
164
|
-
|
|
165
|
-
try:
|
|
166
|
-
write_debug_message(f"[DEBUG-012] 태스크 대기 시작 - task_id={task_record.get('id')}", DEBUG_LEVEL_VERBOSE)
|
|
167
|
-
done, pending = await asyncio.wait(
|
|
168
|
-
[cancel_watch_task, execute_task],
|
|
169
|
-
return_when=asyncio.FIRST_COMPLETED
|
|
170
|
-
)
|
|
171
|
-
write_debug_message(f"[DEBUG-013] 태스크 완료 감지 - done_count={len(done)}, pending_count={len(pending)}", DEBUG_LEVEL_VERBOSE)
|
|
172
|
-
for task in pending:
|
|
173
|
-
write_debug_message(f"[DEBUG-014] 대기 중인 태스크 취소 - task_name={task.get_name()}", DEBUG_LEVEL_VERBOSE)
|
|
174
|
-
task.cancel()
|
|
175
|
-
|
|
176
|
-
except Exception as e:
|
|
177
|
-
handle_application_error("서비스 실행 오류", e, raise_error=False)
|
|
178
|
-
cancel_watch_task.cancel()
|
|
179
|
-
execute_task.cancel()
|
|
180
|
-
finally:
|
|
181
|
-
# 컨텍스트 정리
|
|
182
|
-
try:
|
|
183
|
-
reset_context()
|
|
184
|
-
except Exception as e:
|
|
185
|
-
handle_application_error("컨텍스트 리셋 실패", e, raise_error=False)
|
|
186
|
-
try:
|
|
187
|
-
await event_queue.close()
|
|
188
|
-
except Exception as e:
|
|
189
|
-
handle_application_error("이벤트 큐 종료 실패", e, raise_error=False)
|
|
190
|
-
write_log_message(f"[EXEC END] task_id={task_record.get('id')} agent={prepared_data.get('agent_orch','')}")
|
|
191
|
-
|
|
192
|
-
async def _watch_cancellation(self, task_record: Dict[str, Any], executor: AgentExecutor, context: RequestContext, event_queue: EventQueue, execute_task: asyncio.Task) -> None:
|
|
193
|
-
"""작업 상태를 주기적으로 확인해 취소 신호 시 안전 종료를 수행."""
|
|
194
|
-
todo_id = str(task_record.get("id"))
|
|
195
|
-
|
|
196
|
-
while True:
|
|
197
|
-
await asyncio.sleep(self.cancel_check_interval)
|
|
198
|
-
|
|
199
|
-
status = await fetch_task_status(todo_id)
|
|
200
|
-
normalized = (status or "").strip().lower()
|
|
201
|
-
write_debug_message(f"[DEBUG-015] 취소 상태 확인 - todo_id={todo_id}, status='{status}', normalized='{normalized}', check_interval={self.cancel_check_interval}s", DEBUG_LEVEL_VERBOSE)
|
|
202
|
-
if normalized in ("cancelled", "fb_requested"):
|
|
203
|
-
write_log_message(f"작업 취소 감지: {todo_id}, 상태: {status}")
|
|
204
|
-
write_debug_message(f"[DEBUG-016] 취소 처리 시작 - todo_id={todo_id}, status='{status}', normalized='{normalized}'", DEBUG_LEVEL_BASIC)
|
|
205
|
-
|
|
206
|
-
try:
|
|
207
|
-
await executor.cancel(context, event_queue)
|
|
208
|
-
except Exception as e:
|
|
209
|
-
handle_application_error("취소 처리 실패", e, raise_error=False)
|
|
210
|
-
finally:
|
|
211
|
-
try:
|
|
212
|
-
execute_task.cancel()
|
|
213
|
-
except Exception as e:
|
|
214
|
-
handle_application_error("실행 태스크 즉시 취소 실패", e, raise_error=False)
|
|
215
|
-
try:
|
|
216
|
-
await event_queue.close()
|
|
217
|
-
except Exception as e:
|
|
218
|
-
handle_application_error("취소 후 이벤트 큐 종료 실패", e, raise_error=False)
|
|
219
|
-
break
|
|
220
|
-
|
|
221
|
-
# =============================================================================
|
|
222
|
-
# 요청 컨텍스트: ProcessGPTRequestContext
|
|
223
|
-
# 설명: 실행기에게 전달되는 요청 데이터/상태를 캡슐화
|
|
224
|
-
# =============================================================================
|
|
225
|
-
class ProcessGPTRequestContext(RequestContext):
|
|
226
|
-
def __init__(self, prepared_data: Dict[str, Any]):
|
|
227
|
-
"""실행에 필요한 데이터 묶음을 보관한다."""
|
|
228
|
-
self._prepared_data = prepared_data
|
|
229
|
-
self._message = prepared_data.get("message", "")
|
|
230
|
-
self._current_task = None
|
|
231
|
-
|
|
232
|
-
def get_user_input(self) -> str:
|
|
233
|
-
"""사용자 입력 메시지를 반환한다."""
|
|
234
|
-
return self._message
|
|
235
|
-
|
|
236
|
-
@property
|
|
237
|
-
def message(self) -> str:
|
|
238
|
-
"""현재 메시지(사용자 입력)를 반환한다."""
|
|
239
|
-
return self._message
|
|
240
|
-
|
|
241
|
-
@property
|
|
242
|
-
def current_task(self):
|
|
243
|
-
"""현재 실행 중 태스크(있다면)를 반환한다."""
|
|
244
|
-
return getattr(self, "_current_task", None)
|
|
245
|
-
|
|
246
|
-
def get_context_data(self) -> Dict[str, Any]:
|
|
247
|
-
"""실행 컨텍스트 전체 데이터를 dict로 반환한다."""
|
|
248
|
-
return self._prepared_data
|
|
249
|
-
|
|
250
|
-
# =============================================================================
|
|
251
|
-
# 이벤트 큐: ProcessGPTEventQueue
|
|
252
|
-
# 설명: 실행기 이벤트를 내부 큐에 넣고, 비동기 처리 태스크를 생성해 저장 로직 호출
|
|
253
|
-
# =============================================================================
|
|
254
|
-
class ProcessGPTEventQueue(EventQueue):
|
|
255
|
-
def __init__(self, task_record: Dict[str, Any], loop: asyncio.AbstractEventLoop | None = None):
|
|
256
|
-
"""현재 처리 중인 작업 레코드를 보관한다."""
|
|
257
|
-
self.todo = task_record
|
|
258
|
-
self._loop = loop
|
|
259
|
-
super().__init__()
|
|
260
|
-
|
|
261
|
-
def enqueue_event(self, event: Event):
|
|
262
|
-
"""이벤트를 큐에 넣고, 백그라운드로 DB 저장 코루틴을 실행한다."""
|
|
263
|
-
try:
|
|
264
|
-
write_debug_message(f"[DEBUG-017] 이벤트 큐 삽입 시작 - todo_id={self.todo.get('id')}, event_type={type(event).__name__}", DEBUG_LEVEL_VERBOSE)
|
|
265
|
-
try:
|
|
266
|
-
super().enqueue_event(event)
|
|
267
|
-
write_debug_message(f"[DEBUG-018] 이벤트 큐 삽입 성공 - todo_id={self.todo.get('id')}, event_type={type(event).__name__}", DEBUG_LEVEL_VERBOSE)
|
|
268
|
-
except Exception as e:
|
|
269
|
-
write_debug_message(f"[DEBUG-019] 이벤트 큐 삽입 실패 - todo_id={self.todo.get('id')}, error={str(e)}", DEBUG_LEVEL_BASIC)
|
|
270
|
-
handle_application_error("이벤트 큐 삽입 실패", e, raise_error=False)
|
|
271
|
-
|
|
272
|
-
write_debug_message(f"[DEBUG-020] 백그라운드 이벤트 처리 태스크 생성 - todo_id={self.todo.get('id')}", DEBUG_LEVEL_VERBOSE)
|
|
273
|
-
self._create_bg_task(process_event_message(self.todo, event), "process_event_message")
|
|
274
|
-
except Exception as e:
|
|
275
|
-
write_debug_message(f"[DEBUG-021] 이벤트 저장 전체 실패 - todo_id={self.todo.get('id')}, error={str(e)}", DEBUG_LEVEL_BASIC)
|
|
276
|
-
handle_application_error("이벤트 저장 실패", e, raise_error=False)
|
|
277
|
-
|
|
278
|
-
def task_done(self) -> None:
|
|
279
|
-
"""태스크 완료 로그를 남긴다."""
|
|
280
|
-
try:
|
|
281
|
-
write_log_message(f"태스크 완료: {self.todo['id']}")
|
|
282
|
-
except Exception as e:
|
|
283
|
-
handle_application_error("태스크 완료 처리 실패", e, raise_error=False)
|
|
284
|
-
|
|
285
|
-
async def close(self) -> None:
|
|
286
|
-
"""큐 종료 훅(필요 시 리소스 정리)."""
|
|
287
|
-
pass
|
|
288
|
-
|
|
289
|
-
def _create_bg_task(self, coro: Any, label: str) -> None:
|
|
290
|
-
"""백그라운드 태스크 생성 및 완료 콜백으로 예외 로깅.
|
|
291
|
-
|
|
292
|
-
- 실행 중인 이벤트 루프가 없을 때도 전달된 루프에 안전하게 예약한다.
|
|
293
|
-
"""
|
|
294
|
-
try:
|
|
295
|
-
loop = self._loop
|
|
296
|
-
if loop is None:
|
|
297
|
-
try:
|
|
298
|
-
loop = asyncio.get_running_loop()
|
|
299
|
-
except RuntimeError:
|
|
300
|
-
raise
|
|
301
|
-
|
|
302
|
-
def _cb(t: asyncio.Task):
|
|
303
|
-
exc = t.exception()
|
|
304
|
-
if exc:
|
|
305
|
-
handle_application_error(f"백그라운드 태스크 오류({label})", exc, raise_error=False)
|
|
306
|
-
|
|
307
|
-
def _schedule():
|
|
308
|
-
task = loop.create_task(coro)
|
|
309
|
-
task.add_done_callback(_cb)
|
|
310
|
-
|
|
311
|
-
loop.call_soon_threadsafe(_schedule)
|
|
312
|
-
except Exception as e:
|
|
313
|
-
handle_application_error(f"백그라운드 태스크 생성 실패({label})", e, raise_error=False)
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import json
|
|
3
|
-
import uuid
|
|
4
|
-
from datetime import datetime, timezone
|
|
5
|
-
from typing import Any, Dict, List, Optional
|
|
6
|
-
import sys
|
|
7
|
-
|
|
8
|
-
from a2a.server.agent_execution import AgentExecutor, RequestContext
|
|
9
|
-
from a2a.server.events import EventQueue, Event
|
|
10
|
-
|
|
11
|
-
from .utils.logger import handle_application_error, write_log_message
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# =============================================================================
|
|
15
|
-
# 시뮬레이터: ProcessGPTAgentSimulator
|
|
16
|
-
# 설명: 데이터베이스 연결 없이 작업을 시뮬레이션하는 핵심 시뮬레이터
|
|
17
|
-
# =============================================================================
|
|
18
|
-
class ProcessGPTAgentSimulator:
|
|
19
|
-
"""ProcessGPT 시뮬레이터
|
|
20
|
-
|
|
21
|
-
- 데이터베이스 연결 없이 에이전트 실행을 시뮬레이션
|
|
22
|
-
- CLI로 프롬프트를 받아 실행하고 진행상태를 stdout으로 출력
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
def __init__(self, executor: AgentExecutor, agent_orch: str = ""):
|
|
26
|
-
"""시뮬레이터 실행기/오케스트레이션 값을 초기화한다."""
|
|
27
|
-
self.is_running = False
|
|
28
|
-
self._executor: AgentExecutor = executor
|
|
29
|
-
self.agent_orch: str = agent_orch or "simulator"
|
|
30
|
-
self.task_id = str(uuid.uuid4())
|
|
31
|
-
self.proc_inst_id = str(uuid.uuid4())
|
|
32
|
-
|
|
33
|
-
async def run_simulation(self, prompt: str, **kwargs) -> None:
|
|
34
|
-
"""단일 작업을 시뮬레이션한다."""
|
|
35
|
-
self.is_running = True
|
|
36
|
-
write_log_message("ProcessGPT 시뮬레이터 시작")
|
|
37
|
-
write_log_message(f"작업 시뮬레이션 시작: {self.task_id}")
|
|
38
|
-
|
|
39
|
-
try:
|
|
40
|
-
# 시뮬레이션용 작업 레코드 생성
|
|
41
|
-
task_record = self._create_mock_task_record(prompt, **kwargs)
|
|
42
|
-
|
|
43
|
-
# 서비스 데이터 준비 (모든 것을 모킹)
|
|
44
|
-
prepared_data = self._prepare_mock_service_data(task_record)
|
|
45
|
-
write_log_message(f"시뮬레이션 데이터 준비 완료 [task_id={self.task_id} agent={prepared_data.get('agent_orch','')}]")
|
|
46
|
-
|
|
47
|
-
# 실행
|
|
48
|
-
await self._execute_simulation(task_record, prepared_data)
|
|
49
|
-
write_log_message(f"시뮬레이션 실행 완료 [task_id={self.task_id}]")
|
|
50
|
-
|
|
51
|
-
except Exception as e:
|
|
52
|
-
handle_application_error("시뮬레이션 처리 오류", e, raise_error=False)
|
|
53
|
-
finally:
|
|
54
|
-
self.is_running = False
|
|
55
|
-
write_log_message("ProcessGPT 시뮬레이터 종료")
|
|
56
|
-
|
|
57
|
-
def _create_mock_task_record(self, prompt: str, **kwargs) -> Dict[str, Any]:
|
|
58
|
-
"""시뮬레이션용 작업 레코드를 생성한다."""
|
|
59
|
-
return {
|
|
60
|
-
"id": self.task_id,
|
|
61
|
-
"proc_inst_id": self.proc_inst_id,
|
|
62
|
-
"agent_orch": self.agent_orch,
|
|
63
|
-
"description": prompt,
|
|
64
|
-
"activity_name": kwargs.get("activity_name", "simulation_task"),
|
|
65
|
-
"user_id": kwargs.get("user_id", str(uuid.uuid4())),
|
|
66
|
-
"tenant_id": kwargs.get("tenant_id", str(uuid.uuid4())),
|
|
67
|
-
"tool": kwargs.get("tool", "default"),
|
|
68
|
-
"feedback": kwargs.get("feedback", ""),
|
|
69
|
-
"created_at": datetime.now(timezone.utc).isoformat(),
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
def _prepare_mock_service_data(self, task_record: Dict[str, Any]) -> Dict[str, Any]:
|
|
73
|
-
"""시뮬레이션용 서비스 데이터를 준비한다."""
|
|
74
|
-
# 모킹된 에이전트 데이터
|
|
75
|
-
mock_agents = [
|
|
76
|
-
{
|
|
77
|
-
"id": str(uuid.uuid4()),
|
|
78
|
-
"name": "simulation_agent",
|
|
79
|
-
"role": "AI Assistant",
|
|
80
|
-
"goal": "Simulate task execution",
|
|
81
|
-
"persona": "A helpful AI assistant for simulation",
|
|
82
|
-
"tools": "mem0",
|
|
83
|
-
"profile": "Simulation Agent",
|
|
84
|
-
"model": "gpt-4",
|
|
85
|
-
"tenant_id": task_record.get("tenant_id"),
|
|
86
|
-
}
|
|
87
|
-
]
|
|
88
|
-
|
|
89
|
-
# 모킹된 폼 데이터
|
|
90
|
-
mock_form_types = [
|
|
91
|
-
{"key": "default", "type": "text", "text": "Default form field"}
|
|
92
|
-
]
|
|
93
|
-
|
|
94
|
-
# 모킹된 MCP 설정
|
|
95
|
-
mock_mcp_config = {
|
|
96
|
-
"enabled": True,
|
|
97
|
-
"tools": ["mem0", "search"],
|
|
98
|
-
"config": {}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
prepared: Dict[str, Any] = {
|
|
102
|
-
"task_id": str(task_record.get("id")),
|
|
103
|
-
"proc_inst_id": task_record.get("proc_inst_id"),
|
|
104
|
-
"agent_list": mock_agents,
|
|
105
|
-
"mcp_config": mock_mcp_config,
|
|
106
|
-
"form_id": "default",
|
|
107
|
-
"form_types": mock_form_types,
|
|
108
|
-
"form_html": "<form>Default simulation form</form>",
|
|
109
|
-
"activity_name": str(task_record.get("activity_name", "")),
|
|
110
|
-
"message": str(task_record.get("description", "")),
|
|
111
|
-
"agent_orch": str(task_record.get("agent_orch", "")),
|
|
112
|
-
"done_outputs": [],
|
|
113
|
-
"output_summary": "",
|
|
114
|
-
"feedback_summary": "",
|
|
115
|
-
"all_users": "simulation@example.com",
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return prepared
|
|
119
|
-
|
|
120
|
-
async def _execute_simulation(self, task_record: Dict[str, Any], prepared_data: Dict[str, Any]) -> None:
|
|
121
|
-
"""시뮬레이션 실행을 수행한다."""
|
|
122
|
-
executor = self._executor
|
|
123
|
-
|
|
124
|
-
context = SimulatorRequestContext(prepared_data)
|
|
125
|
-
event_queue = SimulatorEventQueue(task_record)
|
|
126
|
-
|
|
127
|
-
write_log_message(f"시뮬레이션 실행 시작 [task_id={task_record.get('id')} agent={prepared_data.get('agent_orch','')}]")
|
|
128
|
-
|
|
129
|
-
try:
|
|
130
|
-
await executor.execute(context, event_queue)
|
|
131
|
-
except Exception as e:
|
|
132
|
-
handle_application_error("시뮬레이터 실행 오류", e, raise_error=False)
|
|
133
|
-
finally:
|
|
134
|
-
try:
|
|
135
|
-
await event_queue.close()
|
|
136
|
-
except Exception as e:
|
|
137
|
-
handle_application_error("시뮬레이터 이벤트 큐 종료 실패", e, raise_error=False)
|
|
138
|
-
write_log_message(f"시뮬레이션 실행 종료 [task_id={task_record.get('id')}]")
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
# =============================================================================
|
|
142
|
-
# 시뮬레이터 요청 컨텍스트: SimulatorRequestContext
|
|
143
|
-
# 설명: 시뮬레이터용 요청 데이터/상태를 캡슐화
|
|
144
|
-
# =============================================================================
|
|
145
|
-
class SimulatorRequestContext(RequestContext):
|
|
146
|
-
def __init__(self, prepared_data: Dict[str, Any]):
|
|
147
|
-
"""시뮬레이션 실행에 필요한 데이터 묶음을 보관한다."""
|
|
148
|
-
self._prepared_data = prepared_data
|
|
149
|
-
self._message = prepared_data.get("message", "")
|
|
150
|
-
self._current_task = None
|
|
151
|
-
|
|
152
|
-
def get_user_input(self) -> str:
|
|
153
|
-
"""사용자 입력 메시지를 반환한다."""
|
|
154
|
-
return self._message
|
|
155
|
-
|
|
156
|
-
@property
|
|
157
|
-
def message(self) -> str:
|
|
158
|
-
"""현재 메시지(사용자 입력)를 반환한다."""
|
|
159
|
-
return self._message
|
|
160
|
-
|
|
161
|
-
@property
|
|
162
|
-
def current_task(self):
|
|
163
|
-
"""현재 실행 중 태스크(있다면)를 반환한다."""
|
|
164
|
-
return getattr(self, "_current_task", None)
|
|
165
|
-
|
|
166
|
-
def get_context_data(self) -> Dict[str, Any]:
|
|
167
|
-
"""실행 컨텍스트 전체 데이터를 dict로 반환한다."""
|
|
168
|
-
return self._prepared_data
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
# =============================================================================
|
|
172
|
-
# 시뮬레이터 이벤트 큐: SimulatorEventQueue
|
|
173
|
-
# 설명: 시뮬레이터용 이벤트를 stdout으로 출력
|
|
174
|
-
# =============================================================================
|
|
175
|
-
class SimulatorEventQueue(EventQueue):
|
|
176
|
-
def __init__(self, task_record: Dict[str, Any]):
|
|
177
|
-
"""현재 처리 중인 작업 레코드를 보관한다."""
|
|
178
|
-
self.todo = task_record
|
|
179
|
-
super().__init__()
|
|
180
|
-
|
|
181
|
-
def enqueue_event(self, event: Event):
|
|
182
|
-
"""이벤트를 큐에 넣고, stdout으로 진행상태를 출력한다."""
|
|
183
|
-
try:
|
|
184
|
-
super().enqueue_event(event)
|
|
185
|
-
|
|
186
|
-
# 이벤트를 stdout으로 출력
|
|
187
|
-
event_data = self._convert_event_to_dict(event)
|
|
188
|
-
self._output_event_to_stdout(event_data)
|
|
189
|
-
|
|
190
|
-
except Exception as e:
|
|
191
|
-
handle_application_error("시뮬레이터 이벤트 처리 실패", e, raise_error=False)
|
|
192
|
-
|
|
193
|
-
def _convert_event_to_dict(self, event: Event) -> Dict[str, Any]:
|
|
194
|
-
"""이벤트를 딕셔너리로 변환한다."""
|
|
195
|
-
try:
|
|
196
|
-
if hasattr(event, "__dict__"):
|
|
197
|
-
return {k: v for k, v in event.__dict__.items() if not k.startswith("_")}
|
|
198
|
-
return {"event": str(event)}
|
|
199
|
-
except Exception as e:
|
|
200
|
-
handle_application_error("시뮬레이터 이벤트 변환 실패", e, raise_error=False)
|
|
201
|
-
return {"event": str(event)}
|
|
202
|
-
|
|
203
|
-
def _output_event_to_stdout(self, event_data: Dict[str, Any]) -> None:
|
|
204
|
-
"""이벤트 데이터를 stdout으로 출력한다."""
|
|
205
|
-
try:
|
|
206
|
-
# 타임스탬프 추가
|
|
207
|
-
output_data = {
|
|
208
|
-
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
209
|
-
"task_id": self.todo.get("id"),
|
|
210
|
-
"proc_inst_id": self.todo.get("proc_inst_id"),
|
|
211
|
-
"event": event_data
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
# JSON 형태로 stdout에 출력
|
|
215
|
-
json_output = json.dumps(output_data, ensure_ascii=False, indent=2)
|
|
216
|
-
print(f"[EVENT] {json_output}", file=sys.stdout, flush=True)
|
|
217
|
-
|
|
218
|
-
except Exception as e:
|
|
219
|
-
handle_application_error("stdout 출력 실패", e, raise_error=False)
|
|
220
|
-
|
|
221
|
-
def task_done(self) -> None:
|
|
222
|
-
"""태스크 완료 로그를 남긴다."""
|
|
223
|
-
try:
|
|
224
|
-
write_log_message(f"시뮬레이션 태스크 완료: {self.todo['id']}")
|
|
225
|
-
self._output_event_to_stdout({"type": "task_completed", "message": "Task simulation completed"})
|
|
226
|
-
except Exception as e:
|
|
227
|
-
handle_application_error("시뮬레이터 태스크 완료 처리 실패", e, raise_error=False)
|
|
228
|
-
|
|
229
|
-
async def close(self) -> None:
|
|
230
|
-
"""큐 종료 훅."""
|
|
231
|
-
self._output_event_to_stdout({"type": "queue_closed", "message": "Event queue closed"})
|
|
File without changes
|