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
6 changes: 3 additions & 3 deletions src/fireflyframework_genai/studio/api/oracle.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,9 @@ def _get_canvas() -> dict[str, Any]:
# Send response in chunks for frontend streaming effect
import asyncio

_CHUNK_SIZE = 12
for i in range(0, len(full_text), _CHUNK_SIZE):
chunk = full_text[i : i + _CHUNK_SIZE]
_chunk_size = 12
for i in range(0, len(full_text), _chunk_size):
chunk = full_text[i : i + _chunk_size]
await websocket.send_json(
{"type": "oracle_token", "content": chunk}
)
Expand Down
8 changes: 4 additions & 4 deletions src/fireflyframework_genai/studio/api/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,17 +262,17 @@ async def add_service(req: dict) -> dict:

# Preserve existing secret values when the incoming value is the
# mask placeholder ("***") sent by list_services.
_MASK = "***"
_SECRET_FIELDS = ("password", "connection_url", "api_key", "token")
_mask = "***"
_secret_fields = ("password", "connection_url", "api_key", "token")
incoming_id = req.get("id")
if incoming_id:
old_sc = next(
(s for s in settings.service_credentials if s.id == incoming_id),
None,
)
if old_sc is not None:
for field in _SECRET_FIELDS:
if req.get(field) == _MASK:
for field in _secret_fields:
if req.get(field) == _mask:
existing_val = getattr(old_sc, field, None)
if existing_val is not None:
req[field] = (
Expand Down
38 changes: 19 additions & 19 deletions src/fireflyframework_genai/studio/assistant/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,29 +110,29 @@ async def add_node(
if node_type not in _VALID_NODE_TYPES:
raise ValueError(f"Invalid node_type '{node_type}'. Must be one of: {', '.join(sorted(_VALID_NODE_TYPES))}")

H_GAP = 300
V_GAP = 150
START_X = 250
START_Y = 250
h_gap = 300
v_gap = 150
start_x = 250
start_y = 250

if x == 0.0 and y == 0.0:
if not canvas.nodes:
x, y = START_X, START_Y
x, y = start_x, start_y
else:
occupied = {
(int(n.position.get("x", 0)), int(n.position.get("y", 0)))
for n in canvas.nodes
}
rightmost = max(canvas.nodes, key=lambda n: n.position.get("x", 0))
x = rightmost.position.get("x", 0) + H_GAP
y = rightmost.position.get("y", START_Y)
x = rightmost.position.get("x", 0) + h_gap
y = rightmost.position.get("y", start_y)

# Avoid vertical collision: offset downward if position is taken
while any(
abs(ox - x) < 100 and abs(oy - y) < 80
for ox, oy in occupied
):
y += V_GAP
y += v_gap

node = CanvasNode(
id=canvas.next_id(node_type),
Expand Down Expand Up @@ -376,10 +376,10 @@ async def auto_layout() -> str:
if not canvas.nodes:
return "Canvas is empty."

H_GAP = 300
V_GAP = 150
START_X = 250
START_Y = 250
h_gap = 300
v_gap = 150
start_x = 250
start_y = 250

node_ids = {n.id for n in canvas.nodes}

Expand Down Expand Up @@ -412,16 +412,16 @@ async def auto_layout() -> str:
if remaining:
layers.append(sorted(remaining))

# Assign positions: x = layer index * H_GAP, y centered in layer
# Assign positions: x = layer index * h_gap, y centered in layer
node_map = {n.id: n for n in canvas.nodes}
for layer_idx, layer in enumerate(layers):
x = START_X + layer_idx * H_GAP
total_height = (len(layer) - 1) * V_GAP
start_y = START_Y - total_height / 2
x = start_x + layer_idx * h_gap
total_height = (len(layer) - 1) * v_gap
layer_y = start_y - total_height / 2
for pos_idx, nid in enumerate(layer):
node = node_map.get(nid)
if node:
node.position = {"x": float(x), "y": float(start_y + pos_idx * V_GAP)}
node.position = {"x": float(x), "y": float(layer_y + pos_idx * v_gap)}

return json.dumps({
"status": "layout_complete",
Expand Down Expand Up @@ -635,15 +635,15 @@ async def get_tool_status() -> str:
settings = load_settings()
tc = settings.tool_credentials

_TOOL_CREDENTIAL_MAP: dict[str, list[str]] = {
_tool_credential_map: dict[str, list[str]] = {
"search": ["serpapi_api_key", "serper_api_key", "tavily_api_key"],
"database": ["database_url"],
"custom:slack": ["slack_bot_token"],
"custom:telegram": ["telegram_bot_token"],
}

results = []
for tool_name, required_creds in _TOOL_CREDENTIAL_MAP.items():
for tool_name, required_creds in _tool_credential_map.items():
configured = [
c for c in required_creds
if getattr(tc, c, None)
Expand Down
10 changes: 5 additions & 5 deletions src/fireflyframework_genai/studio/assistant/oracle.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,11 +334,11 @@ async def get_pipeline_stats() -> str:
for n in nodes:
data = n.get("data", n.get("config", {}))
ntype = n.get("type", "")
if ntype == "agent" and data.get("model"):
configured += 1
elif ntype == "tool" and data.get("tool_name"):
configured += 1
elif ntype not in ("agent", "tool"):
if (
(ntype == "agent" and data.get("model"))
or (ntype == "tool" and data.get("tool_name"))
or ntype not in ("agent", "tool")
):
configured += 1

return json.dumps({
Expand Down
6 changes: 1 addition & 5 deletions src/fireflyframework_genai/studio/codegen/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,11 +571,7 @@ def _step_expression(node: GraphNode) -> str:
return f"CallableStep({name}_validate)"
elif node.type == NodeType.CUSTOM_CODE:
return f"CallableStep({name}_execute)"
elif node.type == NodeType.INPUT:
return f"CallableStep({name}_step)"
elif node.type == NodeType.OUTPUT:
return f"CallableStep({name}_step)"
elif node.type == NodeType.PIPELINE_STEP:
elif node.type in (NodeType.INPUT, NodeType.OUTPUT, NodeType.PIPELINE_STEP):
return f"CallableStep({name}_step)"
else:
return f"# Unknown node type: {node.type}"
Expand Down
2 changes: 1 addition & 1 deletion src/fireflyframework_genai/studio/execution/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def compile_graph(
if input_nodes:
if len(input_nodes) > 1:
raise CompilationError(
"Pipeline must have exactly one Input node, found {}.".format(len(input_nodes))
f"Pipeline must have exactly one Input node, found {len(input_nodes)}."
)
if not output_nodes:
raise CompilationError(
Expand Down
4 changes: 1 addition & 3 deletions tests/test_studio/test_dynamic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

import json

import pytest

from fireflyframework_genai.studio.codegen.generator import generate_python, _get_default_model
from fireflyframework_genai.studio.codegen.generator import _get_default_model, generate_python
from fireflyframework_genai.studio.codegen.models import GraphModel, GraphNode, NodeType


Expand Down
12 changes: 6 additions & 6 deletions tests/test_studio/test_graphql.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@
pytest.importorskip("fastapi", reason="fastapi not installed")
pytest.importorskip("httpx", reason="httpx not installed")

import httpx

from fireflyframework_genai.studio.config import StudioConfig
from fireflyframework_genai.studio.server import create_studio_app
import httpx # noqa: E402

from fireflyframework_genai.studio.config import StudioConfig # noqa: E402
from fireflyframework_genai.studio.server import create_studio_app # noqa: E402

# ---------------------------------------------------------------------------
# Fixtures
Expand Down Expand Up @@ -229,9 +228,10 @@ def _mock_import(name: str, *args: object, **kwargs: object) -> object:

reload(gql_mod)

from fireflyframework_genai.studio.projects import ProjectManager
from pathlib import Path
import tempfile
from pathlib import Path

from fireflyframework_genai.studio.projects import ProjectManager

with tempfile.TemporaryDirectory() as td:
pm = ProjectManager(Path(td))
Expand Down
6 changes: 1 addition & 5 deletions tests/test_studio/test_pipeline_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@

from unittest.mock import AsyncMock, MagicMock

import pytest

from fireflyframework_genai.agents.registry import agent_registry
from fireflyframework_genai.pipeline.engine import PipelineEngine
from fireflyframework_genai.studio.codegen.models import (
GraphEdge,
GraphModel,
Expand Down Expand Up @@ -217,7 +213,7 @@ async def test_node_error_event(self):
graph = _make_graph([node])

engine = compile_graph(graph, event_handler=handler)
result = await engine.run(inputs="")
await engine.run(inputs="")

events = handler.drain_events()
error_events = [e for e in events if e["type"] == "node_error"]
Expand Down
7 changes: 1 addition & 6 deletions tests/test_studio/test_pipeline_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@

from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from fireflyframework_genai.agents.registry import agent_registry
from fireflyframework_genai.pipeline.context import PipelineContext
from fireflyframework_genai.pipeline.engine import PipelineEngine
Expand All @@ -43,10 +41,7 @@
GraphNode,
NodeType,
)
from fireflyframework_genai.studio.execution.compiler import (
CompilationError,
compile_graph,
)
from fireflyframework_genai.studio.execution.compiler import compile_graph
from fireflyframework_genai.tools.registry import tool_registry

# ---------------------------------------------------------------------------
Expand Down
1 change: 0 additions & 1 deletion tests/test_studio/test_project_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
from fireflyframework_genai.studio.config import StudioConfig
from fireflyframework_genai.studio.server import create_studio_app


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------
Expand Down
Loading