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.
package/client/package.json
CHANGED
package/package.json
CHANGED
package/server/services/ai.py
CHANGED
|
@@ -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 —
|
|
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
|
-
#
|
|
26
|
-
# ``
|
|
27
|
-
#
|
|
28
|
-
#
|
|
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
|
-
#
|
|
32
|
-
# ``
|
|
33
|
-
#
|
|
34
|
-
#
|
|
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
|
-
#
|
|
37
|
-
#
|
|
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
|
-
|
|
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
|
|