ltcai 5.6.0 → 6.1.0
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.
- package/README.md +45 -25
- package/docs/CHANGELOG.md +74 -0
- package/docs/V4_1_FRONTEND_MIGRATION_REPORT.md +1 -1
- package/docs/V4_6_1_RELEASE_REFRESH_REPORT.md +1 -1
- package/docs/V4_7_0_ADMIN_SEPARATION_REPORT.md +1 -1
- package/docs/V4_7_1_ADMIN_OPERATIONS_REPORT.md +1 -1
- package/frontend/openapi.json +39 -0
- package/frontend/src/App.tsx +5 -0
- package/frontend/src/api/client.ts +104 -23
- package/frontend/src/api/openapi.ts +48 -0
- package/frontend/src/components/FirstRunGuide.tsx +3 -3
- package/frontend/src/components/ProductFlow.tsx +7 -0
- package/frontend/src/features/review/ReviewCard.tsx +96 -0
- package/frontend/src/features/review/ReviewInbox.tsx +112 -0
- package/frontend/src/features/review/reviewHelpers.ts +69 -0
- package/frontend/src/i18n.ts +18 -8
- package/frontend/src/pages/Act.tsx +5 -177
- package/frontend/src/routes.ts +1 -0
- package/frontend/src/styles.css +20 -0
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/chat.py +52 -33
- package/latticeai/api/review_queue.py +7 -3
- package/latticeai/app_factory.py +253 -475
- package/latticeai/cli/__init__.py +1 -0
- package/latticeai/cli/runtime.py +37 -0
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/runtime/app_context_runtime.py +13 -0
- package/latticeai/runtime/automation_runtime.py +64 -0
- package/latticeai/runtime/bootstrap.py +48 -0
- package/latticeai/runtime/context_runtime.py +43 -0
- package/latticeai/runtime/hooks_runtime.py +77 -0
- package/latticeai/runtime/lifespan_runtime.py +138 -0
- package/latticeai/runtime/persistence_runtime.py +87 -0
- package/latticeai/runtime/platform_services_runtime.py +39 -0
- package/latticeai/runtime/router_registration.py +570 -0
- package/latticeai/runtime/web_runtime.py +65 -0
- package/latticeai/services/app_context.py +1 -0
- package/latticeai/services/review_queue.py +20 -4
- package/latticeai/services/tool_dispatch.py +82 -25
- package/ltcai_cli.py +5 -31
- package/package.json +1 -1
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +5 -5
- package/static/app/assets/{index-xRn29gI8.css → index-B744yblP.css} +1 -1
- package/static/app/assets/index-DYaUKNfl.js +16 -0
- package/static/app/assets/index-DYaUKNfl.js.map +1 -0
- package/static/app/index.html +2 -2
- package/telegram_bot.py +3 -9
- package/static/app/assets/index-xMFu94cX.js +0 -16
- package/static/app/assets/index-xMFu94cX.js.map +0 -1
package/latticeai/api/chat.py
CHANGED
|
@@ -150,6 +150,35 @@ async def single_text_stream(text: str, model: str = "system") -> AsyncIterator[
|
|
|
150
150
|
yield "data: [DONE]\n\n"
|
|
151
151
|
|
|
152
152
|
|
|
153
|
+
def build_recent_chat_context(
|
|
154
|
+
*,
|
|
155
|
+
get_history,
|
|
156
|
+
limit: int = 10,
|
|
157
|
+
include_image_missing_replies: bool = True,
|
|
158
|
+
user_email: Optional[str] = None,
|
|
159
|
+
conversation_id: Optional[str] = None,
|
|
160
|
+
) -> str:
|
|
161
|
+
history = get_history()
|
|
162
|
+
if conversation_id:
|
|
163
|
+
history = [item for item in history if item.get("conversation_id") == conversation_id]
|
|
164
|
+
if user_email:
|
|
165
|
+
history = pair_user_history(history, user_email)
|
|
166
|
+
history = history[-limit:]
|
|
167
|
+
lines = []
|
|
168
|
+
for item in history:
|
|
169
|
+
role = item.get("role", "user")
|
|
170
|
+
content = item.get("content", "")
|
|
171
|
+
if not include_image_missing_replies and role == "assistant":
|
|
172
|
+
if "이미지" in content and any(word in content for word in ["업로드", "제공", "올려"]):
|
|
173
|
+
continue
|
|
174
|
+
source = item.get("source")
|
|
175
|
+
label = role
|
|
176
|
+
if source:
|
|
177
|
+
label = f"{role} ({source})"
|
|
178
|
+
lines.append(f"{label}: {content}")
|
|
179
|
+
return "\n".join(lines)
|
|
180
|
+
|
|
181
|
+
|
|
153
182
|
def create_chat_router(context: AppContext) -> APIRouter:
|
|
154
183
|
"""Build the chat/history/agent router from the typed application context.
|
|
155
184
|
|
|
@@ -201,31 +230,19 @@ def create_chat_router(context: AppContext) -> APIRouter:
|
|
|
201
230
|
except Exception as exc:
|
|
202
231
|
logging.warning("chat message bridge failed: %s", exc)
|
|
203
232
|
|
|
204
|
-
def
|
|
233
|
+
def recent_chat_context(
|
|
205
234
|
limit: int = 10,
|
|
206
235
|
include_image_missing_replies: bool = True,
|
|
207
236
|
user_email: Optional[str] = None,
|
|
208
237
|
conversation_id: Optional[str] = None,
|
|
209
238
|
) -> str:
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
for item in history:
|
|
218
|
-
role = item.get("role", "user")
|
|
219
|
-
content = item.get("content", "")
|
|
220
|
-
if not include_image_missing_replies and role == "assistant":
|
|
221
|
-
if "이미지" in content and any(word in content for word in ["업로드", "제공", "올려"]):
|
|
222
|
-
continue
|
|
223
|
-
source = item.get("source")
|
|
224
|
-
label = role
|
|
225
|
-
if source:
|
|
226
|
-
label = f"{role} ({source})"
|
|
227
|
-
lines.append(f"{label}: {content}")
|
|
228
|
-
return "\n".join(lines)
|
|
239
|
+
return build_recent_chat_context(
|
|
240
|
+
get_history=get_history,
|
|
241
|
+
limit=limit,
|
|
242
|
+
include_image_missing_replies=include_image_missing_replies,
|
|
243
|
+
user_email=user_email,
|
|
244
|
+
conversation_id=conversation_id,
|
|
245
|
+
)
|
|
229
246
|
|
|
230
247
|
def extract_screenshot_context(image_data: Optional[str]) -> str:
|
|
231
248
|
if not image_data:
|
|
@@ -283,16 +300,18 @@ def create_chat_router(context: AppContext) -> APIRouter:
|
|
|
283
300
|
|
|
284
301
|
return "\n".join(lines)
|
|
285
302
|
|
|
286
|
-
_AGENT_RUNTIME =
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
303
|
+
_AGENT_RUNTIME = context.chat_agent_runtime
|
|
304
|
+
if _AGENT_RUNTIME is None:
|
|
305
|
+
_AGENT_RUNTIME = build_agent_runtime(
|
|
306
|
+
model_router=router,
|
|
307
|
+
execute_tool=execute_tool,
|
|
308
|
+
recent_chat_context=recent_chat_context,
|
|
309
|
+
clear_history=clear_history,
|
|
310
|
+
knowledge_save=knowledge_save,
|
|
311
|
+
audit=append_audit_event,
|
|
312
|
+
hooks=hooks,
|
|
313
|
+
brain_memory=context.brain_memory,
|
|
314
|
+
)
|
|
296
315
|
|
|
297
316
|
@api_router.post("/chat")
|
|
298
317
|
async def chat(req: ChatRequest, request: Request):
|
|
@@ -546,7 +565,7 @@ def create_chat_router(context: AppContext) -> APIRouter:
|
|
|
546
565
|
return JSONResponse(content={"response": str(result), "trace_id": trace_record["id"], "trace": trace_record})
|
|
547
566
|
|
|
548
567
|
if req.stream:
|
|
549
|
-
recent_context =
|
|
568
|
+
recent_context = recent_chat_context(user_email=effective_email, conversation_id=req.conversation_id)
|
|
550
569
|
stream_context = context
|
|
551
570
|
if recent_context:
|
|
552
571
|
stream_context = f"[RECENT CONVERSATION]\n{recent_context}\n\n{context}".strip()
|
|
@@ -564,7 +583,7 @@ def create_chat_router(context: AppContext) -> APIRouter:
|
|
|
564
583
|
)
|
|
565
584
|
else:
|
|
566
585
|
if req.image_data:
|
|
567
|
-
recent_context =
|
|
586
|
+
recent_context = recent_chat_context(
|
|
568
587
|
limit=6,
|
|
569
588
|
include_image_missing_replies=False,
|
|
570
589
|
user_email=effective_email,
|
|
@@ -572,7 +591,7 @@ def create_chat_router(context: AppContext) -> APIRouter:
|
|
|
572
591
|
)
|
|
573
592
|
full_context = f"[RECENT CONVERSATION]\n{recent_context}\n\n{context}".strip() if recent_context else context
|
|
574
593
|
else:
|
|
575
|
-
history_context =
|
|
594
|
+
history_context = recent_chat_context(user_email=effective_email, conversation_id=req.conversation_id)
|
|
576
595
|
full_context = f"{history_context}\n{context}" if context else history_context
|
|
577
596
|
|
|
578
597
|
result = await router.generate(req.message, full_context, req.max_tokens, req.temperature, req.image_data)
|
|
@@ -7,8 +7,8 @@ frontend without massaging.
|
|
|
7
7
|
|
|
8
8
|
Action semantics live in :class:`~latticeai.services.review_queue.ReviewQueueService`:
|
|
9
9
|
|
|
10
|
-
* ``approve`` / ``dismiss`` / ``snooze`` are status transitions;
|
|
11
|
-
transition returns **409**.
|
|
10
|
+
* ``approve`` / ``dismiss`` / ``snooze`` / ``unsnooze`` are status transitions;
|
|
11
|
+
an illegal transition returns **409**.
|
|
12
12
|
* ``run_now`` previews/regenerates without changing status (back-links the run).
|
|
13
13
|
"""
|
|
14
14
|
|
|
@@ -125,6 +125,10 @@ def create_review_queue_router(
|
|
|
125
125
|
append_audit_event("review_item_snooze", user_email=user, item_id=item_id)
|
|
126
126
|
return item
|
|
127
127
|
|
|
128
|
+
@router.post("/automation/reviews/{item_id}/unsnooze", response_model=ReviewItem)
|
|
129
|
+
async def unsnooze_item(item_id: str, request: Request):
|
|
130
|
+
return _act(request, item_id, "unsnooze")
|
|
131
|
+
|
|
128
132
|
@router.post("/automation/reviews/{item_id}/run_now", response_model=ReviewItem)
|
|
129
133
|
async def run_now_item(item_id: str, request: Request):
|
|
130
134
|
user = require_user(request)
|
|
@@ -155,4 +159,4 @@ def create_review_queue_router(
|
|
|
155
159
|
append_audit_event(f"review_item_{action}", user_email=user, item_id=item_id)
|
|
156
160
|
return item
|
|
157
161
|
|
|
158
|
-
return router
|
|
162
|
+
return router
|