Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions backend/app/agent/toolkit/terminal_toolkit.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
get_task_lock,
process_task,
)
from app.utils.file_utils import post_process_gifs_in_directory
from app.utils.listen.toolkit_listen import auto_listen_toolkit

logger = logging.getLogger("terminal_toolkit")
Expand Down Expand Up @@ -384,6 +385,17 @@ def shell_exec(
if block and result == "":
return "Command executed successfully (no output)."

# Post-process GIF files to ensure infinite loop
if self.working_directory and block:
try:
modified = post_process_gifs_in_directory(self.working_directory)
if modified:
logger.debug(
f"Post-processed {modified} GIF file(s) for infinite loop"
)
except Exception as e:
logger.warning(f"Failed to post-process GIFs: {e}")

return result

def cleanup(self, remove_venv: bool = True):
Expand Down
4 changes: 3 additions & 1 deletion backend/app/service/chat_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
record_workforce_memory_snapshot,
)
from app.utils.event_loop_utils import set_main_event_loop
from app.utils.file_utils import get_working_directory, list_files
from app.utils.file_utils import get_working_directory, list_files, post_process_gifs_in_directory
from app.utils.server.sync_step import sync_step
from app.utils.telemetry.workforce_metrics import WorkforceMetricsCallback
from app.utils.workforce import Workforce
Expand Down Expand Up @@ -257,6 +257,8 @@ def collect_previous_task_context(
skip_extensions=(".pyc", ".tmp"),
skip_prefix=".",
)
# Post-process GIFs to ensure infinite loop
post_process_gifs_in_directory(working_directory)
if generated_files:
context_parts.append("Generated Files from Previous Task:")
for file_path in sorted(generated_files):
Expand Down
84 changes: 84 additions & 0 deletions backend/app/utils/file_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,87 @@ def sync_eigent_skills_to_project(working_directory: str) -> None:
e,
exc_info=True,
)


def ensure_gif_infinite_loop(file_path: str) -> bool:
"""
Ensure a GIF file has infinite loop (loop=0 in PIL).

Args:
file_path: Path to the GIF file

Returns:
True if file was modified, False otherwise
"""
try:
from PIL import Image
except ImportError:
logger.warning("PIL not available, cannot fix GIF loop")
return False

if not os.path.exists(file_path):
return False

if not file_path.lower().endswith('.gif'):
return False

try:
with Image.open(file_path) as img:
current_loop = img.info.get('loop', 1)
# In PIL, loop=0 means infinite loop
if current_loop == 0:
return False # Already infinite

# Need to re-save with loop=0
# GIFs can have multiple frames, so we need to handle that
frames = []
durations = []
try:
while True:
frames.append(img.copy())
durations.append(img.info.get('duration', 100))
img.seek(img.tell() + 1)
except EOFError:
pass

if not frames:
return False

# Save with infinite loop
frames[0].save(
file_path,
format='GIF',
save_all=True,
append_images=frames[1:] if len(frames) > 1 else [],
duration=durations,
loop=0, # 0 = infinite loop
disposal=img.info.get('disposal', 2),
)
logger.debug(f"Fixed GIF loop for {file_path}")
return True
except Exception as e:
logger.warning(f"Failed to fix GIF loop for {file_path}: {e}")
return False


def post_process_gifs_in_directory(directory: str) -> int:
"""
Post-process all GIF files in a directory to ensure infinite loop.

Args:
directory: Directory to scan for GIF files

Returns:
Number of files modified
"""
if not os.path.isdir(directory):
return 0

modified = 0
for root, _, files in os.walk(directory):
for file in files:
if file.lower().endswith('.gif'):
file_path = os.path.join(root, file)
if ensure_gif_infinite_loop(file_path):
modified += 1
return modified
2 changes: 1 addition & 1 deletion server/docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ services:
environment:
POSTGRES_DB: eigent
POSTGRES_USER: postgres
POSTGRES_PASSWORD: 123456
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-123456}
POSTGRES_INITDB_ARGS: '--encoding=UTF-8 --lc-collate=C --lc-ctype=C'
ports:
- '5432:5432'
Expand Down
20 changes: 13 additions & 7 deletions server/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ services:
environment:
POSTGRES_DB: eigent
POSTGRES_USER: postgres
POSTGRES_PASSWORD: 123456
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-123456}
POSTGRES_INITDB_ARGS: '--encoding=UTF-8 --lc-collate=C --lc-ctype=C'
ports:
- '5432:5432'
Expand Down Expand Up @@ -44,15 +44,17 @@ services:
context: ..
dockerfile: server/Dockerfile
args:
database_url: postgresql://postgres:123456@postgres:5432/eigent
# Placeholder for build arg (baked into image metadata) — no secrets
database_url: postgresql://postgres:placeholder@postgres:5432/eigent
container_name: eigent_api
restart: unless-stopped
ports:
- '3001:5678'
env_file:
- .env
environment:
- database_url=postgresql://postgres:123456@postgres:5432/eigent
# Runtime substitution (from .env or POSTGRES_PASSWORD env var)
- database_url=postgresql://postgres:${POSTGRES_PASSWORD:-123456}@postgres:5432/eigent
- redis_url=redis://redis:6379/0
- celery_broker_url=redis://redis:6379/0
- celery_result_url=redis://redis:6379/0
Expand Down Expand Up @@ -82,14 +84,16 @@ services:
context: ..
dockerfile: server/Dockerfile
args:
database_url: postgresql://postgres:123456@postgres:5432/eigent
# Placeholder for build arg (baked into image metadata) — no secrets
database_url: postgresql://postgres:placeholder@postgres:5432/eigent
container_name: eigent_celery_worker
restart: unless-stopped
command: /app/celery/worker/start
env_file:
- .env
environment:
- database_url=postgresql://postgres:123456@postgres:5432/eigent
# Runtime substitution (from .env or POSTGRES_PASSWORD env var)
- database_url=postgresql://postgres:${POSTGRES_PASSWORD:-123456}@postgres:5432/eigent
- redis_url=redis://redis:6379/0
- celery_broker_url=redis://redis:6379/0
- celery_result_url=redis://redis:6379/0
Expand Down Expand Up @@ -120,14 +124,16 @@ services:
context: ..
dockerfile: server/Dockerfile
args:
database_url: postgresql://postgres:123456@postgres:5432/eigent
# Placeholder for build arg (baked into image metadata) — no secrets
database_url: postgresql://postgres:placeholder@postgres:5432/eigent
container_name: eigent_celery_beat
restart: unless-stopped
command: /app/celery/beat/start
env_file:
- .env
environment:
- database_url=postgresql://postgres:123456@postgres:5432/eigent
# Runtime substitution (from .env or POSTGRES_PASSWORD env var)
- database_url=postgresql://postgres:${POSTGRES_PASSWORD:-123456}@postgres:5432/eigent
- redis_url=redis://redis:6379/0
- celery_broker_url=redis://redis:6379/0
- celery_result_url=redis://redis:6379/0
Expand Down
Loading