machinaos 0.0.75 → 0.0.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-flow-client",
3
3
  "private": true,
4
- "version": "0.0.75",
4
+ "version": "0.0.76",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "vite --host 0.0.0.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "machinaos",
3
- "version": "0.0.75",
3
+ "version": "0.0.76",
4
4
  "description": "Open source workflow automation platform with AI agents, React Flow, and n8n-inspired architecture",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -11,45 +11,43 @@ from datetime import datetime
11
11
  from typing import Dict, Any, List, Optional, Callable, Type, TypedDict, Annotated, Sequence, TYPE_CHECKING
12
12
  import operator
13
13
 
14
- _ai_t0 = time.perf_counter()
15
-
16
-
17
- def _ailog(msg):
18
- elapsed = (time.perf_counter() - _ai_t0) * 1000
19
- print(f" ai.py: {msg} ({elapsed:.0f}ms)", flush=True)
20
-
21
-
22
14
  # ---------------------------------------------------------------------------
23
- # LangChain imports — split by cost.
15
+ # LangChain imports — fully lazy except for ``BaseMessage``.
16
+ #
17
+ # Cold-start measurement (Windows, fresh disk):
18
+ # ``from langchain_openai import ChatOpenAI`` ~20.8s (pulls openai SDK + tiktoken)
19
+ # ``from langchain_core.tools/langgraph/pydantic`` ~3.0s (langgraph state-graph init)
20
+ # Total eager cost was ~23.8s on first launch.
24
21
  #
25
- # The heavy ones (``langchain_anthropic`` ~800ms, ``langchain_groq`` ~270ms,
26
- # ``langchain_cerebras`` if installed) are deferred via a lazy provider-class
27
- # resolver. They are only needed when an AI agent actually runs through the
28
- # LangChain agent path; the native LLM SDK (``services/llm/``) handles
29
- # ``execute_chat()`` without LangChain entirely.
22
+ # ``services/llm/`` (the native LLM SDK layer) handles ``execute_chat()`` and
23
+ # ``fetch_models()`` without LangChain. LangChain is only needed on the agent
24
+ # execution path (``execute_agent`` / ``execute_chat_agent``). Deferring the
25
+ # imports until the first agent run trims that 23.8s off server-ready time.
30
26
  #
31
- # The light ones (``langchain_openai``, ``langchain_core.messages``,
32
- # ``langgraph``, ``langchain_core.tools``, ``pydantic``) total ~17ms and are
33
- # tightly coupled to module-level type annotations / dataclasses, so we keep
34
- # them eager. Saves ~1.05s of cold-start time without touching type hints.
27
+ # Only ``BaseMessage`` stays eager: ``AgentState`` (a module-level TypedDict)
28
+ # carries ``Annotated[Sequence[BaseMessage], operator.add]``, which LangGraph
29
+ # resolves via ``typing.get_type_hints()`` when ``StateGraph(AgentState)`` is
30
+ # constructed. ``get_type_hints`` ``eval()``s the string annotations against
31
+ # this module's globals, and ``module.__getattr__`` is NOT consulted by
32
+ # ``eval``, so ``BaseMessage`` must live in the real namespace. Importing
33
+ # ``langchain_core.messages`` standalone is cheap (it doesn't pull tiktoken
34
+ # or the openai SDK).
35
35
  #
36
- # Type-only imports go behind ``TYPE_CHECKING`` so static analyzers see them
37
- # without paying the runtime cost.
36
+ # Everything else is resolved via ``@functools.cache``'d helpers or local
37
+ # imports inside the function/method that needs them. Python's ``sys.modules``
38
+ # cache makes repeated local imports microsecond-cheap after the first.
38
39
  # ---------------------------------------------------------------------------
39
40
 
40
- _ailog("importing langchain_openai...")
41
- from langchain_openai import ChatOpenAI
42
- _ailog("importing langchain_core.messages...")
43
- from langchain_core.messages import HumanMessage, SystemMessage, AIMessage, BaseMessage, ToolMessage
44
- _ailog("importing langgraph + pydantic...")
45
- from langchain_core.tools import StructuredTool
46
- from langgraph.graph import StateGraph, END
47
- from langgraph.errors import GraphRecursionError
48
- from pydantic import BaseModel, Field, create_model
49
- _ailog("eager LangChain imports done")
41
+ from langchain_core.messages import BaseMessage # eager: needed for AgentState type resolution
50
42
  import json
51
43
 
52
44
  if TYPE_CHECKING:
45
+ from langchain_openai import ChatOpenAI # noqa: F401
46
+ from langchain_core.messages import HumanMessage, SystemMessage, AIMessage, ToolMessage # noqa: F401
47
+ from langchain_core.tools import StructuredTool # noqa: F401
48
+ from langgraph.graph import StateGraph, END # noqa: F401
49
+ from langgraph.errors import GraphRecursionError # noqa: F401
50
+ from pydantic import BaseModel, Field, create_model # noqa: F401
53
51
  from langchain_anthropic import ChatAnthropic # noqa: F401
54
52
  from langchain_groq import ChatGroq # noqa: F401
55
53
  from langchain_cerebras import ChatCerebras # noqa: F401
@@ -158,6 +156,7 @@ def _parse_memory_markdown(content: str) -> List[BaseMessage]:
158
156
  ### **Assistant** (timestamp)
159
157
  response content
160
158
  """
159
+ from langchain_core.messages import HumanMessage, AIMessage
161
160
  messages = []
162
161
  pattern = r'### \*\*(Human|Assistant)\*\*[^\n]*\n(.*?)(?=\n### \*\*|$)'
163
162
  for role, text in re.findall(pattern, content, re.DOTALL):
@@ -304,6 +303,7 @@ def _bearer_headers(api_key: str) -> dict:
304
303
  # (OpenAI-compatible). ``model_class_factory`` returns the resolved class
305
304
  # (or None for gemini, whose lazy resolver lives at the call site).
306
305
  def _build_provider_class_map() -> Dict[str, tuple]:
306
+ from langchain_openai import ChatOpenAI
307
307
  mapping: Dict[str, tuple] = {
308
308
  'openai': (ChatOpenAI, _openai_headers),
309
309
  'anthropic': (_get_chat_anthropic(), _anthropic_headers),
@@ -405,6 +405,7 @@ def _resolve_temperature(flattened: dict, model: str, provider: str, thinking_en
405
405
  # (services/llm/) bypasses this entirely.
406
406
  def _build_provider_configs() -> Dict[str, ProviderConfig]:
407
407
  """Build PROVIDER_CONFIGS from llm_defaults.json + provider-class map."""
408
+ from langchain_openai import ChatOpenAI
408
409
  providers = _LLM_DEFAULTS.get("providers", {})
409
410
  configs: Dict[str, ProviderConfig] = {}
410
411
  class_map = _build_provider_class_map()
@@ -550,6 +551,7 @@ def filter_empty_messages(messages: Sequence[BaseMessage]) -> List[BaseMessage]:
550
551
  Returns:
551
552
  Filtered list of messages with empty content removed
552
553
  """
554
+ from langchain_core.messages import AIMessage, SystemMessage, ToolMessage
553
555
  filtered = []
554
556
 
555
557
  for m in messages:
@@ -814,6 +816,7 @@ def create_tool_node(tool_executor: Callable):
814
816
  Note: This returns an async function for use with ainvoke().
815
817
  LangGraph supports async node functions natively.
816
818
  """
819
+ from langchain_core.messages import ToolMessage
817
820
  async def tool_node(state: AgentState) -> Dict[str, Any]:
818
821
  """Execute pending tool calls and return results as ToolMessages."""
819
822
  tool_messages = []
@@ -882,6 +885,7 @@ def build_agent_graph(chat_model, tools: List = None, tool_executor: Callable =
882
885
  tools: Optional list of LangChain tools to bind to the model
883
886
  tool_executor: Optional async callback to execute tools
884
887
  """
888
+ from langgraph.graph import StateGraph, END
885
889
  # Create the graph with our state schema
886
890
  graph = StateGraph(AgentState)
887
891
 
@@ -1263,6 +1267,7 @@ class AIService:
1263
1267
  Returns:
1264
1268
  Configured LangChain chat model instance
1265
1269
  """
1270
+ from langchain_openai import ChatOpenAI
1266
1271
  config = get_provider_configs().get(provider)
1267
1272
  if not config:
1268
1273
  # Provide helpful error for Cerebras if import failed
@@ -1517,6 +1522,7 @@ class AIService:
1517
1522
 
1518
1523
  # --- LangChain fallback path (groq, cerebras) ---
1519
1524
  else:
1525
+ from langchain_core.messages import HumanMessage, SystemMessage
1520
1526
  max_tokens = _resolve_max_tokens(flattened, model, provider)
1521
1527
  temperature = _resolve_temperature(
1522
1528
  flattened, model, provider,
@@ -1616,6 +1622,8 @@ class AIService:
1616
1622
  workflow_id: Optional workflow ID for scoped status broadcasts
1617
1623
  context: Optional execution context with nodes, edges for nested agent delegation
1618
1624
  """
1625
+ from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
1626
+ from langgraph.errors import GraphRecursionError
1619
1627
  start_time = time.time()
1620
1628
  provider = 'unknown'
1621
1629
  model = 'unknown'
@@ -2090,6 +2098,8 @@ class AIService:
2090
2098
  workflow_id: Optional workflow ID for scoped status broadcasts
2091
2099
  context: Optional execution context with nodes, edges for nested agent delegation
2092
2100
  """
2101
+ from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
2102
+ from langgraph.errors import GraphRecursionError
2093
2103
  start_time = time.time()
2094
2104
  provider = 'unknown'
2095
2105
  model = 'unknown'
@@ -2531,6 +2541,7 @@ class AIService:
2531
2541
  Returns:
2532
2542
  Tuple of (StructuredTool, config_dict) or (None, None) on failure
2533
2543
  """
2544
+ from langchain_core.tools import StructuredTool
2534
2545
  # Default tool names matching frontend toolNodes.ts definitions
2535
2546
  DEFAULT_TOOL_NAMES = {
2536
2547
  'calculatorTool': 'calculator',
@@ -2806,6 +2817,7 @@ class AIService:
2806
2817
  Returns:
2807
2818
  Pydantic BaseModel class for the tool's arguments
2808
2819
  """
2820
+ from pydantic import BaseModel, Field
2809
2821
  # Check if we have a database-stored schema config (source of truth)
2810
2822
  db_schema_config = params.get('db_schema_config')
2811
2823
  if db_schema_config:
@@ -2934,6 +2946,7 @@ class AIService:
2934
2946
  }
2935
2947
  }
2936
2948
  """
2949
+ from pydantic import Field, create_model
2937
2950
  fields_config = schema_config.get('fields', {})
2938
2951
  schema_description = schema_config.get('description', 'Tool arguments schema')
2939
2952