feat: Add review events persistence, version display, and auto-versioning system
This commit is contained in:
72
tests/README.md
Normal file
72
tests/README.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Тесты
|
||||
|
||||
Эта папка содержит тестовые скрипты для проверки различных компонентов системы.
|
||||
|
||||
## Тесты стриминга
|
||||
|
||||
### test_simple_graph.py
|
||||
|
||||
Простой тест стриминга LangGraph без реальных данных и БД.
|
||||
|
||||
**Запуск:**
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
$env:PYTHONIOENCODING="utf-8"; ./venv/Scripts/python ../tests/test_simple_graph.py # Windows PowerShell
|
||||
# или
|
||||
python ../tests/test_simple_graph.py # Linux/Mac
|
||||
```
|
||||
|
||||
**Что тестирует:**
|
||||
- Различные режимы стриминга (`updates`, `messages`, `values`, `debug`)
|
||||
- Обработку событий через callback
|
||||
- Формат событий от LangGraph
|
||||
|
||||
### test_langgraph_events.py
|
||||
|
||||
Полный тест с реальным ReviewerAgent и БД.
|
||||
|
||||
**Требования:**
|
||||
- Работающая БД с данными
|
||||
- Существующий Review ID, PR Number, Repository ID
|
||||
- Настроенный `.env` файл
|
||||
|
||||
**Запуск:**
|
||||
|
||||
1. Отредактируйте параметры в файле:
|
||||
```python
|
||||
TEST_REVIEW_ID = 1
|
||||
TEST_PR_NUMBER = 5
|
||||
TEST_REPOSITORY_ID = 1
|
||||
```
|
||||
|
||||
2. Запустите:
|
||||
```bash
|
||||
cd backend
|
||||
python ../tests/test_langgraph_events.py
|
||||
```
|
||||
|
||||
### test_llm_streaming.py
|
||||
|
||||
Тест стриминга LLM messages с реальным Ollama.
|
||||
|
||||
**Требования:**
|
||||
- Ollama запущен (`ollama serve`)
|
||||
- Модель загружена (`ollama pull qwen2.5-coder:3b`)
|
||||
|
||||
**Запуск:**
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
$env:PYTHONIOENCODING="utf-8"; ./venv/Scripts/python ../tests/test_llm_streaming.py # Windows
|
||||
python ../tests/test_llm_streaming.py # Linux/Mac
|
||||
```
|
||||
|
||||
## Добавление новых тестов
|
||||
|
||||
Добавляйте новые тесты в эту папку с префиксом `test_`.
|
||||
|
||||
## Полезные ссылки
|
||||
|
||||
- [TEST_STREAMING.md](../docs/TEST_STREAMING.md) - Детальная документация по тестированию стриминга
|
||||
|
||||
229
tests/test_langgraph_events.py
Normal file
229
tests/test_langgraph_events.py
Normal file
@@ -0,0 +1,229 @@
|
||||
"""
|
||||
Тестовый скрипт для проверки событий LangGraph
|
||||
Запустить: python test_langgraph_events.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from backend.app.database import AsyncSessionLocal
|
||||
from backend.app.agents.reviewer import ReviewerAgent
|
||||
|
||||
|
||||
async def test_streaming():
|
||||
"""Тест стриминга событий от LangGraph"""
|
||||
print("="*80)
|
||||
print("ТЕСТ СТРИМИНГА СОБЫТИЙ LANGGRAPH")
|
||||
print("="*80)
|
||||
|
||||
# Параметры для теста (замените на реальные значения из вашей БД)
|
||||
TEST_REVIEW_ID = 1
|
||||
TEST_PR_NUMBER = 5
|
||||
TEST_REPOSITORY_ID = 1
|
||||
|
||||
print(f"\n📋 Параметры теста:")
|
||||
print(f" Review ID: {TEST_REVIEW_ID}")
|
||||
print(f" PR Number: {TEST_PR_NUMBER}")
|
||||
print(f" Repository ID: {TEST_REPOSITORY_ID}")
|
||||
|
||||
# Создаем сессию БД
|
||||
async with AsyncSessionLocal() as db:
|
||||
print(f"\n✅ Подключение к БД установлено")
|
||||
|
||||
# Создаем агента
|
||||
agent = ReviewerAgent(db)
|
||||
print(f"✅ ReviewerAgent создан")
|
||||
|
||||
# Счетчик событий
|
||||
event_counter = {
|
||||
'total': 0,
|
||||
'updates': 0,
|
||||
'messages': 0,
|
||||
'other': 0
|
||||
}
|
||||
|
||||
# Callback для событий
|
||||
async def on_event(event: dict):
|
||||
event_counter['total'] += 1
|
||||
event_type = event.get('type', 'unknown')
|
||||
|
||||
print(f"\n{'='*80}")
|
||||
print(f"📨 СОБЫТИЕ #{event_counter['total']} - Тип: {event_type}")
|
||||
print(f"{'='*80}")
|
||||
print(f"Полное событие: {event}")
|
||||
print(f"{'='*80}\n")
|
||||
|
||||
print(f"\n🚀 Запуск review с стримингом...\n")
|
||||
|
||||
try:
|
||||
# Запускаем review
|
||||
result = await agent.run_review_stream(
|
||||
review_id=TEST_REVIEW_ID,
|
||||
pr_number=TEST_PR_NUMBER,
|
||||
repository_id=TEST_REPOSITORY_ID,
|
||||
on_event=on_event
|
||||
)
|
||||
|
||||
print(f"\n{'='*80}")
|
||||
print(f"✅ REVIEW ЗАВЕРШЕН")
|
||||
print(f"{'='*80}")
|
||||
print(f"\n📊 Статистика событий:")
|
||||
print(f" Всего событий: {event_counter['total']}")
|
||||
print(f" Updates: {event_counter['updates']}")
|
||||
print(f" Messages: {event_counter['messages']}")
|
||||
print(f" Other: {event_counter['other']}")
|
||||
|
||||
print(f"\n📝 Финальное состояние:")
|
||||
print(f" Status: {result.get('status')}")
|
||||
print(f" Files: {len(result.get('files', []))}")
|
||||
print(f" Comments: {len(result.get('comments', []))}")
|
||||
print(f" Error: {result.get('error')}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ ОШИБКА при выполнении review:")
|
||||
print(f" {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
async def test_raw_graph_streaming():
|
||||
"""Тест RAW стриминга напрямую из графа"""
|
||||
print("\n" + "="*80)
|
||||
print("ТЕСТ RAW СТРИМИНГА НАПРЯМУЮ ИЗ ГРАФА")
|
||||
print("="*80)
|
||||
|
||||
# Параметры для теста
|
||||
TEST_REVIEW_ID = 1
|
||||
TEST_PR_NUMBER = 5
|
||||
TEST_REPOSITORY_ID = 1
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
agent = ReviewerAgent(db)
|
||||
|
||||
initial_state = {
|
||||
"review_id": TEST_REVIEW_ID,
|
||||
"pr_number": TEST_PR_NUMBER,
|
||||
"repository_id": TEST_REPOSITORY_ID,
|
||||
"status": "pending",
|
||||
"files": [],
|
||||
"analyzed_files": [],
|
||||
"comments": [],
|
||||
"error": None,
|
||||
"git_service": None
|
||||
}
|
||||
|
||||
print(f"\n🔍 Тест 1: stream_mode=['updates']")
|
||||
print("-" * 80)
|
||||
|
||||
event_count = 0
|
||||
try:
|
||||
async for event in agent.graph.astream(initial_state, stream_mode=["updates"]):
|
||||
event_count += 1
|
||||
print(f"\n📨 Event #{event_count}")
|
||||
print(f" Type: {type(event)}")
|
||||
print(f" Content: {event}")
|
||||
|
||||
if event_count > 10: # Ограничение для безопасности
|
||||
print("\n⚠️ Остановка после 10 событий")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"\n❌ Ошибка: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
print(f"\n✅ Всего событий 'updates': {event_count}")
|
||||
|
||||
# Тест 2: messages
|
||||
print(f"\n\n🔍 Тест 2: stream_mode=['messages']")
|
||||
print("-" * 80)
|
||||
|
||||
event_count = 0
|
||||
try:
|
||||
# Создаем новый агент для чистого теста
|
||||
agent2 = ReviewerAgent(db)
|
||||
|
||||
async for event in agent2.graph.astream(initial_state, stream_mode=["messages"]):
|
||||
event_count += 1
|
||||
print(f"\n📨 Event #{event_count}")
|
||||
print(f" Type: {type(event)}")
|
||||
print(f" Content: {event}")
|
||||
|
||||
if event_count > 10:
|
||||
print("\n⚠️ Остановка после 10 событий")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"\n❌ Ошибка: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
print(f"\n✅ Всего событий 'messages': {event_count}")
|
||||
|
||||
# Тест 3: updates + messages
|
||||
print(f"\n\n🔍 Тест 3: stream_mode=['updates', 'messages']")
|
||||
print("-" * 80)
|
||||
|
||||
event_count = 0
|
||||
try:
|
||||
agent3 = ReviewerAgent(db)
|
||||
|
||||
async for event in agent3.graph.astream(initial_state, stream_mode=["updates", "messages"]):
|
||||
event_count += 1
|
||||
print(f"\n📨 Event #{event_count}")
|
||||
print(f" Type: {type(event)}")
|
||||
|
||||
# Детальный разбор события
|
||||
if isinstance(event, dict):
|
||||
print(f" Dict keys: {list(event.keys())}")
|
||||
for key, value in event.items():
|
||||
print(f" {key}: {type(value).__name__}")
|
||||
elif isinstance(event, tuple):
|
||||
print(f" Tuple length: {len(event)}")
|
||||
for i, item in enumerate(event):
|
||||
print(f" [{i}]: {type(item).__name__}")
|
||||
else:
|
||||
print(f" Content preview: {str(event)[:200]}")
|
||||
|
||||
if event_count > 10:
|
||||
print("\n⚠️ Остановка после 10 событий")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"\n❌ Ошибка: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
print(f"\n✅ Всего событий 'updates + messages': {event_count}")
|
||||
|
||||
|
||||
async def main():
|
||||
"""Главная функция"""
|
||||
import sys
|
||||
|
||||
print("\n" + "🔬"*40)
|
||||
print("ТЕСТИРОВАНИЕ СОБЫТИЙ LANGGRAPH")
|
||||
print("🔬"*40 + "\n")
|
||||
|
||||
print("Выберите тест:")
|
||||
print("1. Полный review с callback (test_streaming)")
|
||||
print("2. RAW стриминг напрямую из графа (test_raw_graph_streaming)")
|
||||
print("3. Оба теста")
|
||||
|
||||
choice = input("\nВведите номер теста (1/2/3) [по умолчанию: 3]: ").strip() or "3"
|
||||
|
||||
if choice in ["1", "3"]:
|
||||
print("\n" + "▶️"*40)
|
||||
print("ЗАПУСК ТЕСТА 1: Полный review")
|
||||
print("▶️"*40 + "\n")
|
||||
await test_streaming()
|
||||
|
||||
if choice in ["2", "3"]:
|
||||
print("\n" + "▶️"*40)
|
||||
print("ЗАПУСК ТЕСТА 2: RAW стриминг")
|
||||
print("▶️"*40 + "\n")
|
||||
await test_raw_graph_streaming()
|
||||
|
||||
print("\n" + "✅"*40)
|
||||
print("ВСЕ ТЕСТЫ ЗАВЕРШЕНЫ")
|
||||
print("✅"*40 + "\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
137
tests/test_llm_streaming.py
Normal file
137
tests/test_llm_streaming.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""
|
||||
Тест стриминга LLM messages от LangGraph
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from langgraph.graph import StateGraph, END
|
||||
from typing import TypedDict, Annotated
|
||||
import operator
|
||||
from langchain_ollama import OllamaLLM
|
||||
|
||||
|
||||
class TestState(TypedDict):
|
||||
messages: Annotated[list, operator.add]
|
||||
result: str
|
||||
|
||||
|
||||
async def llm_node(state: TestState) -> TestState:
|
||||
"""Нода с LLM вызовом"""
|
||||
print(" [LLM NODE] Вызов LLM...")
|
||||
|
||||
llm = OllamaLLM(
|
||||
model="qwen2.5-coder:3b",
|
||||
base_url="http://localhost:11434",
|
||||
temperature=0.7
|
||||
)
|
||||
|
||||
# Простой промпт для быстрого ответа
|
||||
prompt = "Напиши короткую проверку кода на Python (не более 100 символов)"
|
||||
|
||||
response = await llm.ainvoke(prompt)
|
||||
|
||||
print(f" [LLM NODE] Ответ получен: {response[:50]}...")
|
||||
|
||||
return {
|
||||
"messages": [{"role": "ai", "content": response}],
|
||||
"result": response
|
||||
}
|
||||
|
||||
|
||||
def create_test_graph():
|
||||
"""Создает тестовый граф с LLM"""
|
||||
workflow = StateGraph(TestState)
|
||||
|
||||
workflow.add_node("llm_call", llm_node)
|
||||
|
||||
workflow.set_entry_point("llm_call")
|
||||
workflow.add_edge("llm_call", END)
|
||||
|
||||
return workflow.compile()
|
||||
|
||||
|
||||
async def test_with_llm():
|
||||
"""Тест стриминга с LLM"""
|
||||
print("\n" + "="*80)
|
||||
print("ТЕСТ СТРИМИНГА LLM MESSAGES")
|
||||
print("="*80)
|
||||
|
||||
graph = create_test_graph()
|
||||
|
||||
initial_state: TestState = {
|
||||
"messages": [],
|
||||
"result": ""
|
||||
}
|
||||
|
||||
# Тест: updates + messages
|
||||
print(f"\n🔍 Тест: stream_mode=['updates', 'messages']")
|
||||
print("-" * 80)
|
||||
|
||||
event_count = 0
|
||||
messages_count = 0
|
||||
|
||||
async for event in graph.astream(initial_state, stream_mode=["updates", "messages"]):
|
||||
event_count += 1
|
||||
|
||||
if isinstance(event, tuple) and len(event) >= 2:
|
||||
event_type, event_data = event[0], event[1]
|
||||
|
||||
print(f"\n📨 Event #{event_count}")
|
||||
print(f" Type: {event_type}")
|
||||
print(f" Data type: {type(event_data)}")
|
||||
|
||||
if event_type == 'updates':
|
||||
print(f" ✅ Node update")
|
||||
if isinstance(event_data, dict):
|
||||
for node_name in event_data.keys():
|
||||
print(f" Node: {node_name}")
|
||||
|
||||
elif event_type == 'messages':
|
||||
messages_count += 1
|
||||
print(f" 💬 LLM Messages (#{messages_count})")
|
||||
|
||||
if isinstance(event_data, (list, tuple)):
|
||||
for i, msg in enumerate(event_data):
|
||||
print(f" Message {i+1}:")
|
||||
|
||||
# Извлекаем контент
|
||||
if hasattr(msg, 'content'):
|
||||
content = msg.content
|
||||
print(f" Content: {content[:100]}...")
|
||||
elif isinstance(msg, dict):
|
||||
print(f" Dict: {msg}")
|
||||
else:
|
||||
print(f" Type: {type(msg)}")
|
||||
print(f" Str: {str(msg)[:100]}...")
|
||||
|
||||
print(f"\n" + "="*80)
|
||||
print(f"✅ Всего событий: {event_count}")
|
||||
print(f"✅ Messages событий: {messages_count}")
|
||||
print("="*80)
|
||||
|
||||
|
||||
async def main():
|
||||
print("\n" + "="*80)
|
||||
print("ТЕСТИРОВАНИЕ LLM STREAMING В LANGGRAPH")
|
||||
print("="*80)
|
||||
print("\nПроверка Ollama...")
|
||||
|
||||
try:
|
||||
# Проверяем что Ollama доступен
|
||||
from langchain_ollama import OllamaLLM
|
||||
test_llm = OllamaLLM(model="qwen2.5-coder:3b", base_url="http://localhost:11434")
|
||||
result = await test_llm.ainvoke("test")
|
||||
print("✅ Ollama работает!")
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка подключения к Ollama: {e}")
|
||||
print("\n⚠️ Убедитесь что Ollama запущен: ollama serve")
|
||||
print("⚠️ И модель загружена: ollama pull qwen2.5-coder:3b\n")
|
||||
return
|
||||
|
||||
await test_with_llm()
|
||||
|
||||
print("\n✅ Тестирование завершено\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
228
tests/test_simple_graph.py
Normal file
228
tests/test_simple_graph.py
Normal file
@@ -0,0 +1,228 @@
|
||||
"""
|
||||
Упрощенный тест LangGraph без реального review
|
||||
Проверяет только механизм стриминга событий
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from langgraph.graph import StateGraph, END
|
||||
from typing import TypedDict, Annotated, Any
|
||||
import operator
|
||||
|
||||
|
||||
# Простое состояние для теста
|
||||
class SimpleState(TypedDict):
|
||||
counter: Annotated[int, operator.add]
|
||||
messages: list[str]
|
||||
step: str
|
||||
|
||||
|
||||
# Простые ноды
|
||||
async def node_1(state: SimpleState) -> SimpleState:
|
||||
"""Первая нода"""
|
||||
print(" [NODE 1] Выполняется...")
|
||||
await asyncio.sleep(0.5)
|
||||
return {
|
||||
"counter": 1,
|
||||
"messages": ["Node 1 completed"],
|
||||
"step": "node_1"
|
||||
}
|
||||
|
||||
|
||||
async def node_2(state: SimpleState) -> SimpleState:
|
||||
"""Вторая нода"""
|
||||
print(" [NODE 2] Выполняется...")
|
||||
await asyncio.sleep(0.5)
|
||||
return {
|
||||
"counter": 1,
|
||||
"messages": ["Node 2 completed"],
|
||||
"step": "node_2"
|
||||
}
|
||||
|
||||
|
||||
async def node_3(state: SimpleState) -> SimpleState:
|
||||
"""Третья нода"""
|
||||
print(" [NODE 3] Выполняется...")
|
||||
await asyncio.sleep(0.5)
|
||||
return {
|
||||
"counter": 1,
|
||||
"messages": ["Node 3 completed"],
|
||||
"step": "node_3"
|
||||
}
|
||||
|
||||
|
||||
def create_test_graph():
|
||||
"""Создает тестовый граф"""
|
||||
workflow = StateGraph(SimpleState)
|
||||
|
||||
# Добавляем ноды
|
||||
workflow.add_node("node_1", node_1)
|
||||
workflow.add_node("node_2", node_2)
|
||||
workflow.add_node("node_3", node_3)
|
||||
|
||||
# Определяем связи
|
||||
workflow.set_entry_point("node_1")
|
||||
workflow.add_edge("node_1", "node_2")
|
||||
workflow.add_edge("node_2", "node_3")
|
||||
workflow.add_edge("node_3", END)
|
||||
|
||||
return workflow.compile()
|
||||
|
||||
|
||||
async def test_stream_modes():
|
||||
"""Тестирует разные режимы стриминга"""
|
||||
graph = create_test_graph()
|
||||
|
||||
initial_state: SimpleState = {
|
||||
"counter": 0,
|
||||
"messages": [],
|
||||
"step": "start"
|
||||
}
|
||||
|
||||
# Тест 1: updates
|
||||
print("\n" + "="*80)
|
||||
print("ТЕСТ 1: stream_mode=['updates']")
|
||||
print("="*80)
|
||||
|
||||
event_count = 0
|
||||
async for event in graph.astream(initial_state, stream_mode=["updates"]):
|
||||
event_count += 1
|
||||
print(f"\n📨 Event #{event_count}")
|
||||
print(f" Type: {type(event)}")
|
||||
print(f" Content: {event}")
|
||||
|
||||
print(f"\n✅ Получено событий: {event_count}")
|
||||
|
||||
# Тест 2: messages
|
||||
print("\n" + "="*80)
|
||||
print("ТЕСТ 2: stream_mode=['messages']")
|
||||
print("="*80)
|
||||
|
||||
event_count = 0
|
||||
async for event in graph.astream(initial_state, stream_mode=["messages"]):
|
||||
event_count += 1
|
||||
print(f"\n📨 Event #{event_count}")
|
||||
print(f" Type: {type(event)}")
|
||||
print(f" Content: {event}")
|
||||
|
||||
print(f"\n✅ Получено событий: {event_count}")
|
||||
|
||||
# Тест 3: updates + messages
|
||||
print("\n" + "="*80)
|
||||
print("ТЕСТ 3: stream_mode=['updates', 'messages']")
|
||||
print("="*80)
|
||||
|
||||
event_count = 0
|
||||
async for event in graph.astream(initial_state, stream_mode=["updates", "messages"]):
|
||||
event_count += 1
|
||||
print(f"\n📨 Event #{event_count}")
|
||||
print(f" Type: {type(event)}")
|
||||
|
||||
if isinstance(event, dict):
|
||||
print(f" Keys: {list(event.keys())}")
|
||||
elif isinstance(event, tuple):
|
||||
print(f" Tuple[0] type: {type(event[0])}")
|
||||
print(f" Tuple[1] type: {type(event[1])}")
|
||||
|
||||
print(f" Content: {event}")
|
||||
|
||||
print(f"\n✅ Получено событий: {event_count}")
|
||||
|
||||
# Тест 4: values
|
||||
print("\n" + "="*80)
|
||||
print("ТЕСТ 4: stream_mode=['values']")
|
||||
print("="*80)
|
||||
|
||||
event_count = 0
|
||||
async for event in graph.astream(initial_state, stream_mode=["values"]):
|
||||
event_count += 1
|
||||
print(f"\n📨 Event #{event_count}")
|
||||
print(f" Type: {type(event)}")
|
||||
print(f" Content: {event}")
|
||||
|
||||
print(f"\n✅ Получено событий: {event_count}")
|
||||
|
||||
# Тест 5: debug (все режимы)
|
||||
print("\n" + "="*80)
|
||||
print("ТЕСТ 5: stream_mode=['updates', 'messages', 'values', 'debug']")
|
||||
print("="*80)
|
||||
|
||||
event_count = 0
|
||||
async for event in graph.astream(
|
||||
initial_state,
|
||||
stream_mode=["updates", "messages", "values", "debug"]
|
||||
):
|
||||
event_count += 1
|
||||
print(f"\n📨 Event #{event_count}")
|
||||
print(f" Type: {type(event)}")
|
||||
|
||||
if isinstance(event, tuple) and len(event) >= 2:
|
||||
print(f" Event type (tuple[0]): {event[0]}")
|
||||
print(f" Event data (tuple[1]): {event[1]}")
|
||||
else:
|
||||
print(f" Content: {event}")
|
||||
|
||||
print(f"\n✅ Получено событий: {event_count}")
|
||||
|
||||
|
||||
async def test_with_callback():
|
||||
"""Тест с использованием callback для обработки событий"""
|
||||
print("\n" + "="*80)
|
||||
print("ТЕСТ 6: Callback обработка событий")
|
||||
print("="*80)
|
||||
|
||||
graph = create_test_graph()
|
||||
|
||||
initial_state: SimpleState = {
|
||||
"counter": 0,
|
||||
"messages": [],
|
||||
"step": "start"
|
||||
}
|
||||
|
||||
collected_events = []
|
||||
|
||||
async def event_callback(event_type: str, event_data: Any):
|
||||
"""Callback для обработки событий"""
|
||||
collected_events.append({
|
||||
"type": event_type,
|
||||
"data": event_data
|
||||
})
|
||||
print(f" 🔔 Callback: {event_type}")
|
||||
|
||||
# Симуляция обработки событий с callback
|
||||
event_count = 0
|
||||
async for event in graph.astream(initial_state, stream_mode=["updates", "messages"]):
|
||||
event_count += 1
|
||||
print(f"\n📨 Event #{event_count}: {type(event)}")
|
||||
|
||||
# Обрабатываем событие
|
||||
if isinstance(event, tuple) and len(event) >= 2:
|
||||
await event_callback(str(event[0]), event[1])
|
||||
elif isinstance(event, dict):
|
||||
for node_name, node_data in event.items():
|
||||
await event_callback(f"node_update_{node_name}", node_data)
|
||||
else:
|
||||
await event_callback("unknown", event)
|
||||
|
||||
print(f"\n✅ Всего событий: {event_count}")
|
||||
print(f"✅ Callback вызовов: {len(collected_events)}")
|
||||
print(f"\n📋 Собранные события:")
|
||||
for i, evt in enumerate(collected_events, 1):
|
||||
print(f" {i}. {evt['type']}")
|
||||
|
||||
|
||||
async def main():
|
||||
print("\n" + "="*80)
|
||||
print("ПРОСТОЙ ТЕСТ LANGGRAPH STREAMING")
|
||||
print("="*80 + "\n")
|
||||
|
||||
await test_stream_modes()
|
||||
await test_with_callback()
|
||||
|
||||
print("\n" + "="*80)
|
||||
print("ТЕСТИРОВАНИЕ ЗАВЕРШЕНО")
|
||||
print("="*80 + "\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
Reference in New Issue
Block a user