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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/felix_agent_sdk/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.2.0"
__version__ = "0.2.1"
17 changes: 15 additions & 2 deletions src/felix_agent_sdk/visualization/helix_visualizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,21 @@ def render(
day: Current simulation day for the header.
extra_info: Arbitrary key-value pairs shown in the header.
"""
sys.stdout.write(clear_screen())
sys.stdout.write(self.render_to_string(tick=tick, day=day, extra_info=extra_info))
# Ensure stdout can handle Unicode block-drawing characters on Windows
# where the default encoding is cp1252.
if hasattr(sys.stdout, "reconfigure"):
try:
sys.stdout.reconfigure(encoding="utf-8")
except (AttributeError, OSError):
pass # already reconfigured, unsupported, or redirected stream

output = clear_screen() + self.render_to_string(
tick=tick, day=day, extra_info=extra_info
)
try:
sys.stdout.write(output)
except UnicodeEncodeError:
sys.stdout.buffer.write(output.encode("utf-8"))
sys.stdout.flush()

def render_to_string(
Expand Down
1 change: 0 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
CompletionResult,
MessageRole,
ProviderConfig,
StreamChunk,
)


Expand Down
2 changes: 1 addition & 1 deletion tests/integration/test_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import pytest

from felix_agent_sdk.providers.types import ChatMessage, CompletionResult, MessageRole, StreamChunk
from felix_agent_sdk.providers.types import ChatMessage, CompletionResult, MessageRole

# ---------------------------------------------------------------------------
# Skip conditions
Expand Down
3 changes: 1 addition & 2 deletions tests/integration/test_workflow_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

from unittest.mock import MagicMock

import pytest

from felix_agent_sdk.providers.base import BaseProvider
from felix_agent_sdk.providers.types import CompletionResult
Expand Down Expand Up @@ -157,7 +156,7 @@ def test_hub_shutdown(self):
)
workflow = FelixWorkflow(config, provider)

result = workflow.run("Test cleanup")
workflow.run("Test cleanup")

# If cleanup failed we'd get errors on subsequent runs
result2 = workflow.run("Second run")
Expand Down
3 changes: 0 additions & 3 deletions tests/unit/test_anthropic_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

from __future__ import annotations

from contextlib import contextmanager
from typing import Any
from unittest.mock import MagicMock, patch

import pytest
Expand All @@ -20,7 +18,6 @@
ChatMessage,
CompletionResult,
MessageRole,
StreamChunk,
)


Expand Down
3 changes: 2 additions & 1 deletion tests/unit/test_central_post.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,8 @@ def test_multiple_callbacks_for_same_event(self, central_post):

def test_remove_lifecycle_callback(self, central_post):
log = []
cb = lambda aid: log.append(aid)
def cb(aid):
return log.append(aid)
central_post.add_lifecycle_callback(AgentLifecycleEvent.FAILED, cb)
central_post.remove_lifecycle_callback(AgentLifecycleEvent.FAILED, cb)
central_post.emit_lifecycle_event(AgentLifecycleEvent.FAILED, "agent-1")
Expand Down
2 changes: 0 additions & 2 deletions tests/unit/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

from __future__ import annotations

import os
import tempfile
from pathlib import Path

import pytest
Expand Down
1 change: 0 additions & 1 deletion tests/unit/test_compression.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from __future__ import annotations

import json

import pytest

Expand Down
1 change: 0 additions & 1 deletion tests/unit/test_context_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from __future__ import annotations

import pytest

from felix_agent_sdk.workflows.context_builder import (
CollaborativeContextBuilder,
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_dynamic_spawner.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from felix_agent_sdk.communication.central_post import CentralPost
from felix_agent_sdk.communication.spoke import SpokeManager
from felix_agent_sdk.core.helix import HelixConfig
from felix_agent_sdk.events import EventBus, EventType
from felix_agent_sdk.events import EventBus
from felix_agent_sdk.providers.base import BaseProvider
from felix_agent_sdk.providers.types import CompletionResult
from felix_agent_sdk.spawning import ConfidenceMonitor, DynamicSpawner
Expand Down
1 change: 0 additions & 1 deletion tests/unit/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from __future__ import annotations

import threading
import time

import pytest

Expand Down
36 changes: 36 additions & 0 deletions tests/unit/test_helix_visualizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,3 +317,39 @@ def test_footer_without_monitor(self, viz):
assert "Confidence" in output
# Should not crash without monitor
assert "Trend" not in output


# ------------------------------------------------------------------
# Windows encoding fallback
# ------------------------------------------------------------------


class TestRenderEncodingFallback:
def test_render_survives_unicode_encode_error(self, viz, monkeypatch):
"""render() should not crash when stdout can't handle Unicode."""
import io

viz.register_agent("a1", "Agent1", color="cyan")
viz.update("a1", 0.5, 0.8)

# Simulate a stdout that raises UnicodeEncodeError on write
class FakeStdout:
def write(self, s):
raise UnicodeEncodeError("cp1252", s, 0, 1, "bad char")

def flush(self):
... # intentionally empty — test double

buffer = io.BytesIO()

def reconfigure(self, **kwargs):
... # intentionally empty — simulates already-reconfigured

fake = FakeStdout()
monkeypatch.setattr("sys.stdout", fake)

# Should not raise — falls back to buffer.write(utf-8)
viz.render()

# Verify something was written to the buffer
assert fake.buffer.tell() > 0
2 changes: 0 additions & 2 deletions tests/unit/test_knowledge_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

from __future__ import annotations

import time

import pytest

from felix_agent_sdk.memory.knowledge_store import (
ConfidenceLevel,
KnowledgeEntry,
KnowledgeQuery,
KnowledgeStore,
KnowledgeType,
)

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_llm_agent_streaming.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from felix_agent_sdk.agents.llm_agent import LLMAgent, LLMTask
from felix_agent_sdk.core.helix import HelixConfig, HelixGeometry
from felix_agent_sdk.events import EventBus, EventType
from felix_agent_sdk.events import EventBus
from felix_agent_sdk.providers.base import BaseProvider
from felix_agent_sdk.providers.types import CompletionResult, StreamChunk
from felix_agent_sdk.streaming import CallbackStreamHandler, StreamHandler
Expand Down
1 change: 0 additions & 1 deletion tests/unit/test_local_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from unittest.mock import MagicMock, patch

import pytest

from felix_agent_sdk.providers.local import LocalProvider
from felix_agent_sdk.providers.openai_provider import OpenAIProvider
Expand Down
12 changes: 5 additions & 7 deletions tests/unit/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import json
import logging

import pytest

from felix_agent_sdk.events import EventBus, EventType, FelixEvent
from felix_agent_sdk.utils.logging import (
Expand All @@ -25,7 +24,6 @@ def _capture_handler() -> logging.Handler:
"""Return a handler that stores formatted records in handler.records."""
handler = logging.StreamHandler()
handler.records: list[str] = [] # type: ignore[attr-defined]
original_emit = handler.emit

def capturing_emit(record: logging.LogRecord) -> None:
handler.records.append(handler.format(record)) # type: ignore[attr-defined]
Expand Down Expand Up @@ -169,7 +167,7 @@ def test_logs_events(self):
configure_logging(FelixLogConfig(level="DEBUG", output_handler=handler))

bus = EventBus()
bridge = EventLogBridge(bus)
EventLogBridge(bus)

bus.emit(FelixEvent(
event_type=EventType.WORKFLOW_STARTED,
Expand All @@ -184,7 +182,7 @@ def test_json_bridge(self):
configure_logging(FelixLogConfig(level="DEBUG", format="json", output_handler=handler))

bus = EventBus()
bridge = EventLogBridge(bus)
EventLogBridge(bus)

bus.emit(FelixEvent(
event_type=EventType.TASK_COMPLETED,
Expand Down Expand Up @@ -215,7 +213,7 @@ def test_custom_level_map(self):
configure_logging(FelixLogConfig(level="DEBUG", output_handler=handler))

bus = EventBus()
bridge = EventLogBridge(bus, level_map={"custom.event": "WARNING"})
EventLogBridge(bus, level_map={"custom.event": "WARNING"})

bus.emit(FelixEvent(event_type="custom.event", source="test"))

Expand All @@ -226,7 +224,7 @@ def test_default_level_is_debug(self):
configure_logging(FelixLogConfig(level="DEBUG", output_handler=handler))

bus = EventBus()
bridge = EventLogBridge(bus)
EventLogBridge(bus)

bus.emit(FelixEvent(event_type="unknown.event.type", source="test"))

Expand All @@ -237,7 +235,7 @@ def test_workflow_events_log_at_info(self):
configure_logging(FelixLogConfig(level="DEBUG", output_handler=handler))

bus = EventBus()
bridge = EventLogBridge(bus)
EventLogBridge(bus)

bus.emit(FelixEvent(event_type=EventType.WORKFLOW_STARTED, source="wf"))
bus.emit(FelixEvent(event_type=EventType.WORKFLOW_COMPLETED, source="wf"))
Expand Down
1 change: 0 additions & 1 deletion tests/unit/test_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import time
import uuid

import pytest

from felix_agent_sdk.communication.messages import Message, MessageType

Expand Down
1 change: 0 additions & 1 deletion tests/unit/test_openai_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
ChatMessage,
CompletionResult,
MessageRole,
StreamChunk,
)


Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from unittest.mock import MagicMock, patch
from unittest.mock import patch

import pytest

Expand Down
3 changes: 1 addition & 2 deletions tests/unit/test_spoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from unittest.mock import MagicMock

import pytest

from felix_agent_sdk.communication.central_post import CentralPost
from felix_agent_sdk.communication.messages import Message, MessageType
Expand Down Expand Up @@ -384,7 +383,7 @@ def test_broadcast_excludes_specified_ids(self, central_post, spoke_manager):
assert len(confirmations) == 2

def test_broadcast_skips_disconnected_spokes(self, central_post, spoke_manager):
spoke1 = spoke_manager.create_spoke("agent-1")
spoke_manager.create_spoke("agent-1")
spoke2 = spoke_manager.create_spoke("agent-2")
spoke2.disconnect()
confirmations = spoke_manager.broadcast_message(
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_streaming.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest

from felix_agent_sdk.events import EventBus, EventType
from felix_agent_sdk.events import EventBus
from felix_agent_sdk.providers.types import StreamChunk
from felix_agent_sdk.streaming import (
CallbackStreamHandler,
Expand Down
3 changes: 1 addition & 2 deletions tests/unit/test_synthesizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from unittest.mock import MagicMock

import pytest

from felix_agent_sdk.agents.llm_agent import LLMResult
from felix_agent_sdk.providers.base import BaseProvider
Expand Down Expand Up @@ -108,7 +107,7 @@ def test_without_compression(self):
synth = WorkflowSynthesizer(provider, config)

results = [_make_result("a1", "Data", 0.5)]
output = synth.synthesize(results, "task")
synth.synthesize(results, "task")
assert provider.complete.called


Expand Down
2 changes: 0 additions & 2 deletions tests/unit/test_task_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

from __future__ import annotations

import pytest

from felix_agent_sdk.memory.task_memory import (
TaskComplexity,
TaskExecution,
TaskMemory,
TaskMemoryQuery,
TaskOutcome,
TaskPattern,
Expand Down
1 change: 0 additions & 1 deletion tests/unit/test_workflow_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from __future__ import annotations

import pytest

from felix_agent_sdk.core.helix import HelixConfig
from felix_agent_sdk.workflows.config import (
Expand Down
Loading