Merge pull request #256 from Pythagora-io/l-fix

L fix
This commit is contained in:
LeonOstrez
2025-07-03 17:16:52 +01:00
committed by GitHub
8 changed files with 83 additions and 33 deletions

1
.gitignore vendored
View File

@@ -8,6 +8,7 @@ dist/
workspace/
pilot-env/
venv/
data/
.coverage
*.code-workspace

View File

@@ -21,6 +21,7 @@ from core.log import get_logger
from core.telemetry import telemetry
from core.templates.registry import PROJECT_TEMPLATES
from core.ui.base import ProjectStage, pythagora_source, success_source
from core.utils.text import trim_logs
log = get_logger(__name__)
@@ -224,8 +225,17 @@ class TechLead(RelevantFilesMixin, BaseAgent):
# load the previous state, because in this state we have deleted tasks due to epic being completed!
wanted_project_state = await self.state_manager.get_project_state_by_id(self.current_state.prev_state_id)
wanted_project_state.epics[-1]["completed"] = False
self.next_state.epics = wanted_project_state.epics
# Trim logs from existing tasks before adding the new task
if wanted_project_state.tasks:
# Trim logs from all existing tasks
for task in wanted_project_state.tasks:
if task.get("description"):
task["description"] = trim_logs(task["description"])
# Create tasks list with new task (after trimming logs from existing tasks)
self.next_state.tasks = wanted_project_state.tasks + [
{
"id": uuid4().hex,
@@ -238,8 +248,12 @@ class TechLead(RelevantFilesMixin, BaseAgent):
}
]
# Flag tasks as modified so SQLAlchemy knows to save the changes
self.next_state.flag_epics_as_modified()
self.next_state.flag_tasks_as_modified()
await self.ui.send_epics_and_tasks(
self.next_state.current_epic.get("sub_epics", []),
self.next_state.epics[-1].get("sub_epics", []),
self.next_state.tasks,
)

View File

@@ -45,6 +45,7 @@ from core.ui.base import AgentSource, UIBase, UISource
from core.ui.console import PlainConsoleUI
from core.ui.ipc_client import IPCClientUI
from core.ui.virtual import VirtualUI
from core.utils.text import trim_logs
log = get_logger(__name__)
@@ -433,36 +434,6 @@ def get_epic_task_number(state, current_task) -> (int, int):
return epic_num, task_num
def trim_logs(logs: str) -> str:
"""
Trim logs by removing everything after specific marker phrases.
This function cuts off the string at the first occurrence of
"Here are the backend logs" or "Here are the frontend logs".
:param logs: Log text to trim
:return: Trimmed log text with the marker phrase removed
"""
if not logs:
return ""
# Define marker phrases
markers = ["Here are the backend logs", "Here are the frontend logs"]
# Find the first occurrence of any marker
index = float("inf")
for marker in markers:
pos = logs.find(marker)
if pos != -1 and pos < index:
index = pos
# If a marker was found, trim the string
if index != float("inf"):
return logs[:index]
return logs
def get_source_for_history(msg_type: Optional[str] = "", question: Optional[str] = ""):
if question in [TL_EDIT_DEV_PLAN]:
return AgentSource("Tech Lead", "tech-lead")

View File

@@ -951,7 +951,6 @@ class ProjectState(Base):
if task.get("status") == TaskStatus.SKIPPED
else "Done",
]
log.debug(task_histories)
last_task = {}

View File

@@ -10,13 +10,13 @@ import httpx
import tiktoken
from httpx import AsyncClient
from core.cli.helpers import trim_logs
from core.config import LLMConfig, LLMProvider
from core.llm.convo import Convo
from core.llm.request_log import LLMRequestLog, LLMRequestStatus
from core.log import get_logger
from core.state.state_manager import StateManager
from core.ui.base import UIBase, pythagora_source
from core.utils.text import trim_logs
log = get_logger(__name__)
tokenizer = tiktoken.get_encoding("cl100k_base")

View File

@@ -36,6 +36,7 @@ from core.proc.exec_log import ExecLog as ExecLogData
from core.telemetry import telemetry
from core.ui.base import UIBase
from core.ui.base import UserInput as UserInputData
from core.utils.text import trim_logs
if TYPE_CHECKING:
from core.agents.base import BaseAgent
@@ -345,6 +346,27 @@ class StateManager:
await state.delete_after()
await session.commit()
# TODO: this is a temporary fix to unblock users!
# TODO: REMOVE THIS AFTER 1 WEEK FROM THIS COMMIT
# Process tasks before setting current state - trim logs from task descriptions before current task
if state.tasks and state.current_task:
try:
# Find the current task index
current_task_index = state.tasks.index(state.current_task)
# Trim logs from all tasks before the current task
for i in range(current_task_index):
task = state.tasks[i]
if task.get("description"):
task["description"] = trim_logs(task["description"])
# Flag tasks as modified so SQLAlchemy knows to save the changes
state.flag_tasks_as_modified()
except Exception as e:
# Handle any error during log trimming gracefully
log.warning(f"Error during log trimming: {e}, skipping log trimming")
pass
self.current_session = session
self.current_state = state
self.branch = state.branch

1
core/utils/__init__.py Normal file
View File

@@ -0,0 +1 @@
# Utils module for common utility functions

42
core/utils/text.py Normal file
View File

@@ -0,0 +1,42 @@
"""
Text processing utility functions.
"""
def trim_logs(logs: str) -> str:
"""
Trim logs by removing everything after specific marker phrases.
This function cuts off the string at the first occurrence of
"Here are the backend logs" or "Here are the frontend logs".
:param logs: Log text to trim
:return: Trimmed log text with the marker phrase removed
"""
try:
if not logs:
return ""
# Ensure we have a string
if not isinstance(logs, str):
logs = str(logs)
# Define marker phrases
markers = ["Here are the backend logs", "Here are the frontend logs"]
# Find the first occurrence of any marker
index = float("inf")
for marker in markers:
pos = logs.find(marker)
if pos != -1 and pos < index:
index = pos
# If a marker was found, trim the string
if index != float("inf"):
return logs[:index]
return logs
except Exception:
# If anything goes wrong, return the original input as string or empty string
return str(logs) if logs is not None else ""