Skip to content
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 2 additions & 3 deletions backend/giga_agent/agents/experimental/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
Монтируется ядром под `{GIGA_AGENT_PREFIX_API}/experimental` (см.
`ExperimentalModule.get_api_router` и base.py) → `GET /agent/experimental/activity/{thread_id}`.
"""

from __future__ import annotations

from typing import Annotated
Expand Down Expand Up @@ -98,9 +99,7 @@ async def delete_thread(
try:
snap = await client.threads.get_state(thread_id)
except Exception as exc: # noqa: BLE001 — любой сбой = нет доступа
raise HTTPException(
status.HTTP_404_NOT_FOUND, "Thread not found"
) from exc
raise HTTPException(status.HTTP_404_NOT_FOUND, "Thread not found") from exc

values = snap.get("values") or {}
inner_thread_id = values.get("inner_thread_id")
Expand Down
15 changes: 7 additions & 8 deletions backend/giga_agent/agents/experimental/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
ОДИН всплывший элемент отдельным super-step'ом, поэтому число чекпойнтов ≈ числу
всплывших сообщений (несколько на ход), а не по одному на poll-тик.
"""

from __future__ import annotations

import asyncio
Expand Down Expand Up @@ -287,7 +288,7 @@ def _get_rewrite_llm() -> GigaChat:
verify_ssl_certs=False,
profanity_check=True,
streaming=True,
timeout=60
timeout=60,
)
return _rewrite_llm

Expand Down Expand Up @@ -428,7 +429,9 @@ def _ask_questions_messages(interrupt_value: Any, answer: Any) -> list[AnyMessag
сам тул, поэтому он совпадает с inner-результатом. id заглушки детерминирован
(совпадает с фронт-оптимистикой) → без дублей и морганий.
"""
if not (isinstance(interrupt_value, dict) and interrupt_value.get("type") == "questions"):
if not (
isinstance(interrupt_value, dict) and interrupt_value.get("type") == "questions"
):
return []
tcid = interrupt_value.get("tool_call_id") or ""
if not tcid:
Expand Down Expand Up @@ -814,9 +817,7 @@ async def kickoff(state: ExperimentalState, config) -> dict:
else:
with contextlib.suppress(Exception):
snap = await client.threads.get_state(inner_thread_id)
inner_baseline = len(
(snap.get("values") or {}).get("messages") or []
)
inner_baseline = len((snap.get("values") or {}).get("messages") or [])
if is_retry and inner_thread_id:
# Резюмим упавшую inner-таску с последнего чекпойнта: checkpoint-only,
# БЕЗ input/command. command.resume дал бы 400 (тред в статусе "error",
Expand Down Expand Up @@ -857,9 +858,7 @@ async def kickoff(state: ExperimentalState, config) -> dict:
started_at = _now()
await _reset_activity(outer_thread_id, started_at)
await _set_activity_baseline(outer_thread_id, inner_baseline)
marker = _activity_marker_messages(
activity_id, _empty_activity(started_at)
)
marker = _activity_marker_messages(activity_id, _empty_activity(started_at))

return {
"messages": marker,
Expand Down
5 changes: 2 additions & 3 deletions backend/giga_agent/agents/experimental/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
безусловна — так `langgraph.json` остаётся стабильным. Модуль сервисный:
без тулов, инструкций и пустой label (в списке модулей не показывается).
"""

from __future__ import annotations

from typing import Any, Optional, TYPE_CHECKING
Expand All @@ -22,9 +23,7 @@ class ExperimentalModule(BaseModule):
def get_subgraphs(self, **kwargs: Any) -> dict[str, str]:
_ = kwargs
return {
"giga_agent_experimental": (
"giga_agent.agents.experimental.graph:graph"
),
"giga_agent_experimental": ("giga_agent.agents.experimental.graph:graph"),
}

def get_api_router(self, **kwargs: Any) -> Optional["APIRouter"]:
Expand Down
14 changes: 11 additions & 3 deletions backend/giga_agent/channels/telegram/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ async def _flush_media_group(self, group_id: str) -> None:
# The caption/@mention of an album lives on a single part; pick a
# processable part (preferring one with text) as primary so handle_message
# passes its own access check and the agent gets the user's text.
primary = next((m for m in processable if (m.caption or m.text)), processable[0])
primary = next(
(m for m in processable if (m.caption or m.text)), processable[0]
)
extras = [m for m in messages if m is not primary]
try:
await self.handle_message(primary, extra_file_messages=extras)
Expand All @@ -192,8 +194,14 @@ async def handle_message_command(self, message: tg_types.Message) -> None:
"message",
)
has_reply_content = _has_processable_reply_content(message.reply_to_message)
if not command_text and not _has_supported_attachment(message) and not has_reply_content:
await message.answer("После /message нужен текст или вложение для обработки.")
if (
not command_text
and not _has_supported_attachment(message)
and not has_reply_content
):
await message.answer(
"После /message нужен текст или вложение для обработки."
)
return

await self.message_handlers.handle_message(
Expand Down
5 changes: 4 additions & 1 deletion backend/giga_agent/channels/telegram/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ def create_telegram_bot(token: str) -> Bot:
proxy = _get_env_proxy("HTTPS_PROXY", "ALL_PROXY", "HTTP_PROXY")
if proxy:
logger.info("Telegram bot session proxy enabled")
return Bot(token=token, session=AiohttpSession(proxy=_normalize_aiogram_proxy(proxy)))
return Bot(
token=token, session=AiohttpSession(proxy=_normalize_aiogram_proxy(proxy))
)
return Bot(token=token)


__all__ = ["create_telegram_bot"]
8 changes: 6 additions & 2 deletions backend/giga_agent/channels/telegram/handlers/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ async def handle_message(
) -> None:
if not await self.access_service.ensure_supported_chat(message):
return
if not force_process and not self.access_service.should_process_message(message):
if not force_process and not self.access_service.should_process_message(
message
):
return
if contact_message is None:
contact_message = message
Expand Down Expand Up @@ -238,7 +240,9 @@ async def handle_message(
"messages": [human_msg],
"mcp_tools": [build_telegram_message_tool_schema()],
}
collections_payload = await self.thread_service.load_collections_payload()
collections_payload = (
await self.thread_service.load_collections_payload()
)
if collections_payload:
run_input["collections"] = collections_payload

Expand Down
4 changes: 3 additions & 1 deletion backend/giga_agent/channels/telegram/message_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ def _format_message_author(message: tg_types.Message | None) -> tuple[str, str]:
if message is None:
return "unknown", "Unknown"

author = getattr(message, "from_user", None) or getattr(message, "sender_chat", None)
author = getattr(message, "from_user", None) or getattr(
message, "sender_chat", None
)
username = getattr(author, "username", None)
username_value = f"@{username}" if username else "unknown"
name_parts = [
Expand Down
8 changes: 7 additions & 1 deletion backend/giga_agent/channels/telegram/message_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,13 @@ def build_telegram_message_tool_schema() -> dict[str, object]:
},
"kind": {
"type": "string",
"enum": ["image", "document", "audio", "video", "voice"],
"enum": [
"image",
"document",
"audio",
"video",
"voice",
],
"default": "document",
},
"caption": {
Expand Down
6 changes: 4 additions & 2 deletions backend/giga_agent/channels/telegram/services/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ async def send_embedded_media(
reply_markup=markup,
)
sent_any = True
except Exception as exc:
except Exception:
traceback.print_exc()
if kind == "attachment_path":
logger.warning("Failed to send attachment %s", value[:80])
Expand Down Expand Up @@ -557,7 +557,9 @@ async def send_parts_to_chat(
disable_web_page_preview=disable_web_page_preview,
)
except Exception as exc:
logger.exception("Error delivering message to Telegram: %s", exc)
logger.exception(
"Error delivering message to Telegram: %s", exc
)
await self.bot.send_message(
target,
value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ async def get_pending_message_tool_call(
client: Any,
thread_id: str,
) -> dict[str, Any] | None:
pending_tool_calls = await self.get_pending_message_tool_calls(client, thread_id)
pending_tool_calls = await self.get_pending_message_tool_calls(
client, thread_id
)
return pending_tool_calls[-1] if pending_tool_calls else None

async def get_pending_message_tool_calls(
Expand All @@ -149,7 +151,9 @@ async def get_pending_message_tool_calls(
try:
thread_state = await client.threads.get_state(thread_id)
except Exception:
logger.debug("Failed to fetch thread state for %s", thread_id, exc_info=True)
logger.debug(
"Failed to fetch thread state for %s", thread_id, exc_info=True
)
return []
return _find_pending_message_tool_calls(thread_state)

Expand All @@ -165,7 +169,9 @@ async def send_message_tool_prompt(
reply_kwargs = build_reply_kwargs(reply_to_message_id)
sent_attachment_paths: set[str] = set()
for attachment in prompt.attachments:
file_bytes = await self.media_service.download_attachment(token, attachment.path)
file_bytes = await self.media_service.download_attachment(
token, attachment.path
)
if not file_bytes:
continue
filename = attachment.filename or attachment.path.rsplit("/", 1)[-1]
Expand Down Expand Up @@ -391,7 +397,9 @@ async def resume_pending_message_tool(
) -> dict[str, Any] | None:
pending_tool_call = pending_tool_calls[-1]
reply_message = getattr(message, "reply_to_message", None)
reply_text = reply_message.text or reply_message.caption or "" if reply_message else ""
reply_text = (
reply_message.text or reply_message.caption or "" if reply_message else ""
)
reply_file_data: list[dict[str, Any]] = []
if reply_message is not None:
reply_file_data = await self.media_service.collect_incoming_files(
Expand Down
11 changes: 6 additions & 5 deletions backend/giga_agent/channels/telegram/services/threads.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
from langgraph_sdk import get_client
from langgraph_sdk.errors import NotFoundError

from giga_agent.channels.telegram.constants import ASSISTANT_ID, THREAD_TTL_SECONDS, \
TELEGRAM_CHANNEL_TYPE
from giga_agent.channels.telegram.constants import (
ASSISTANT_ID,
THREAD_TTL_SECONDS,
TELEGRAM_CHANNEL_TYPE,
)
from giga_agent.channels.telegram.runtime import get_thread_external_user_id
from giga_agent.channels.telegram.utils import _langgraph_url, _make_token
from giga_agent.core.cache import setup_cache
Expand All @@ -29,9 +32,7 @@ def __init__(self, *, bot_row: ChannelBot, user_email: str):
self.user_email = user_email

def _thread_lock_key(self, chat_id: int, external_user_id: str | None) -> str:
return (
f"channel:tg-thread:{self.bot_row.id}:{chat_id}:{external_user_id}"
)
return f"channel:tg-thread:{self.bot_row.id}:{chat_id}:{external_user_id}"

@property
def assistant_id(self) -> str:
Expand Down
1 change: 0 additions & 1 deletion backend/giga_agent/cli/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
from __future__ import annotations

3 changes: 2 additions & 1 deletion backend/giga_agent/cli/commands/_at_file_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ def _build_index(self) -> list[str]:

for dirpath, dirnames, filenames in walker:
dirnames[:] = [
d for d in dirnames
d
for d in dirnames
if d not in EXCLUDED_DIR_NAMES and not d.startswith(".")
]

Expand Down
6 changes: 2 additions & 4 deletions backend/giga_agent/cli/commands/_langgraph_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@
LANGGRAPH_DEFAULT_DEPENDENCIES = ["."]


def collect_run_server_graphs(
*, agent, base_graph_target: str
) -> dict[str, str]:
def collect_run_server_graphs(*, agent, base_graph_target: str) -> dict[str, str]:
# Import lazily to avoid package import cycles.
cli = importlib.import_module("giga_agent.cli")

graphs: dict[str, str] = {
"giga_agent": base_graph_target,
"giga_agent_channel": base_graph_target
"giga_agent_channel": base_graph_target,
}

for module in agent.all_modules:
Expand Down
4 changes: 3 additions & 1 deletion backend/giga_agent/cli/commands/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ def check(
migration_scope.version_table,
)
else:
logger.info("OK: scope '%s' has no revisions yet.", migration_scope.scope_id)
logger.info(
"OK: scope '%s' has no revisions yet.", migration_scope.scope_id
)

check_db_is_up_to_date(
cfg,
Expand Down
Loading
Loading