lmnr 0.2.8__py3-none-any.whl → 0.2.9__py3-none-any.whl

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.
@@ -4,21 +4,16 @@ from typing import Optional
4
4
  import uuid
5
5
 
6
6
 
7
- HandleType = str # "String" | "ChatMessageList" | "Any"
8
-
9
-
10
7
  @dataclass
11
8
  class Handle:
12
9
  id: uuid.UUID
13
10
  name: Optional[str]
14
- type: HandleType
15
11
 
16
12
  @classmethod
17
13
  def from_dict(cls, dict: dict) -> "Handle":
18
14
  return cls(
19
15
  id=uuid.UUID(dict["id"]),
20
16
  name=(dict["name"] if "name" in dict else None),
21
- type=dict["type"],
22
17
  )
23
18
 
24
19
 
@@ -12,6 +12,8 @@ class CodeNode(NodeFunctions):
12
12
  inputs: list[Handle]
13
13
  outputs: list[Handle]
14
14
  inputs_mappings: dict[uuid.UUID, uuid.UUID]
15
+ code: str
16
+ fn_name: str
15
17
 
16
18
  def handles_mapping(
17
19
  self, output_handle_id_to_node_name: dict[str, str]
@@ -24,4 +26,11 @@ class CodeNode(NodeFunctions):
24
26
  return "Code"
25
27
 
26
28
  def config(self) -> dict:
27
- return {}
29
+ return {
30
+ "code": self.code,
31
+ "fn_name": self.fn_name,
32
+ "fn_inputs": ", ".join(
33
+ f"{handle.name}=input_to_code_node_arg({handle.name})"
34
+ for handle in self.inputs
35
+ ),
36
+ }
@@ -2,7 +2,7 @@ from dataclasses import dataclass
2
2
  from typing import Optional
3
3
  import uuid
4
4
 
5
- from lmnr.cli.parser.nodes import Handle, HandleType, NodeFunctions
5
+ from lmnr.cli.parser.nodes import Handle, NodeFunctions
6
6
  from lmnr.types import NodeInput
7
7
 
8
8
 
@@ -12,7 +12,6 @@ class InputNode(NodeFunctions):
12
12
  name: str
13
13
  outputs: list[Handle]
14
14
  input: Optional[NodeInput]
15
- input_type: HandleType
16
15
 
17
16
  def handles_mapping(
18
17
  self, output_handle_id_to_node_name: dict[str, str]
@@ -0,0 +1,29 @@
1
+ from dataclasses import dataclass
2
+
3
+ import uuid
4
+
5
+ from lmnr.cli.parser.nodes import Handle, NodeFunctions
6
+ from lmnr.cli.parser.utils import map_handles
7
+
8
+
9
+ @dataclass
10
+ class JsonExtractorNode(NodeFunctions):
11
+ id: uuid.UUID
12
+ name: str
13
+ inputs: list[Handle]
14
+ outputs: list[Handle]
15
+ inputs_mappings: dict[uuid.UUID, uuid.UUID]
16
+ template: str
17
+
18
+ def handles_mapping(
19
+ self, output_handle_id_to_node_name: dict[str, str]
20
+ ) -> list[tuple[str, str]]:
21
+ return map_handles(
22
+ self.inputs, self.inputs_mappings, output_handle_id_to_node_name
23
+ )
24
+
25
+ def node_type(self) -> str:
26
+ return "JsonExtractor"
27
+
28
+ def config(self) -> dict:
29
+ return {"template": self.template}
@@ -44,8 +44,13 @@ class LLMNode(NodeFunctions):
44
44
  "model": model,
45
45
  "model_params": self.model_params,
46
46
  "stream": self.stream,
47
- "structured_output_enabled": self.structured_output_enabled,
47
+ "enable_structured_output": self.structured_output_enabled
48
+ and self.structured_output_schema is not None,
48
49
  "structured_output_max_retries": self.structured_output_max_retries,
49
50
  "structured_output_schema": self.structured_output_schema,
50
- "structured_output_schema_target": self.structured_output_schema_target,
51
+ "structured_output_schema_target_str": (
52
+ "None"
53
+ if self.structured_output_schema_target is None
54
+ else f'"{self.structured_output_schema_target}"'
55
+ ),
51
56
  }
@@ -5,6 +5,7 @@ from lmnr.cli.parser.nodes import Handle
5
5
  from lmnr.cli.parser.nodes.code import CodeNode
6
6
  from lmnr.cli.parser.nodes.condition import ConditionNode
7
7
  from lmnr.cli.parser.nodes.input import InputNode
8
+ from lmnr.cli.parser.nodes.json_extractor import JsonExtractorNode
8
9
  from lmnr.cli.parser.nodes.llm import LLMNode
9
10
  from lmnr.cli.parser.nodes.output import OutputNode
10
11
  from lmnr.cli.parser.nodes.router import Route, RouterNode
@@ -32,6 +33,7 @@ Node = Union[
32
33
  RouterNode,
33
34
  SemanticSearchNode,
34
35
  CodeNode,
36
+ JsonExtractorNode,
35
37
  ]
36
38
 
37
39
 
@@ -42,7 +44,6 @@ def node_from_dict(node_dict: dict) -> Node:
42
44
  name=node_dict["name"],
43
45
  outputs=[Handle.from_dict(handle) for handle in node_dict["outputs"]],
44
46
  input=node_input_from_json(node_dict["input"]),
45
- input_type=node_dict["inputType"],
46
47
  )
47
48
  elif node_dict["type"] == "Output":
48
49
  return OutputNode(
@@ -86,11 +87,14 @@ def node_from_dict(node_dict: dict) -> Node:
86
87
  node_dict["modelParams"] if "modelParams" in node_dict else None
87
88
  ),
88
89
  stream=False,
89
- # TODO: Implement structured output
90
- structured_output_enabled=False,
91
- structured_output_max_retries=3,
92
- structured_output_schema=None,
93
- structured_output_schema_target=None,
90
+ structured_output_enabled=node_dict.get("structuredOutputEnabled", False),
91
+ structured_output_max_retries=node_dict.get(
92
+ "structuredOutputMaxRetries", 0
93
+ ),
94
+ structured_output_schema=node_dict.get("structuredOutputSchema", None),
95
+ structured_output_schema_target=node_dict.get(
96
+ "structuredOutputSchemaTarget", None
97
+ ),
94
98
  )
95
99
  elif node_dict["type"] == "Router":
96
100
  return RouterNode(
@@ -130,6 +134,20 @@ def node_from_dict(node_dict: dict) -> Node:
130
134
  uuid.UUID(k): uuid.UUID(v)
131
135
  for k, v in node_dict["inputsMappings"].items()
132
136
  },
137
+ code=node_dict["code"],
138
+ fn_name=node_dict["fnName"],
139
+ )
140
+ elif node_dict["type"] == "JsonExtractor":
141
+ return JsonExtractorNode(
142
+ id=uuid.UUID(node_dict["id"]),
143
+ name=node_dict["name"],
144
+ inputs=[Handle.from_dict(handle) for handle in node_dict["inputs"]],
145
+ outputs=[Handle.from_dict(handle) for handle in node_dict["outputs"]],
146
+ inputs_mappings={
147
+ uuid.UUID(k): uuid.UUID(v)
148
+ for k, v in node_dict["inputsMappings"].items()
149
+ },
150
+ template=node_dict["template"],
133
151
  )
134
152
  else:
135
153
  raise ValueError(f"Node type {node_dict['type']} not supported")
lmnr/cli/parser/parser.py CHANGED
@@ -1,3 +1,4 @@
1
+ from lmnr.cli.parser.utils import replace_spaces_with_underscores
1
2
  from .nodes.types import node_from_dict
2
3
 
3
4
 
@@ -8,6 +9,9 @@ def runnable_graph_to_template_vars(graph: dict) -> dict:
8
9
  node_id_to_node_name = {}
9
10
  output_handle_id_to_node_name: dict[str, str] = {}
10
11
  for node in graph["nodes"].values():
12
+ # override node names in the graph itself to be safe
13
+ node["name"] = replace_spaces_with_underscores(node["name"])
14
+
11
15
  node_id_to_node_name[node["id"]] = node["name"]
12
16
  for handle in node["outputs"]:
13
17
  output_handle_id_to_node_name[handle["id"]] = node["name"]
lmnr/cli/parser/utils.py CHANGED
@@ -23,3 +23,27 @@ def map_handles(
23
23
  (input_name, output_handle_id_to_node_name[str(output_id)])
24
24
  for input_name, output_id in mapping
25
25
  ]
26
+
27
+
28
+ def replace_spaces_with_underscores(s: str):
29
+ spaces = [
30
+ "\u0020",
31
+ "\u00A0",
32
+ "\u1680",
33
+ "\u2000",
34
+ "\u2001",
35
+ "\u2002",
36
+ "\u2003",
37
+ "\u2004",
38
+ "\u2005",
39
+ "\u2006",
40
+ "\u2007",
41
+ "\u2008",
42
+ "\u2009",
43
+ "\u200A",
44
+ "\u200B",
45
+ "\u202F",
46
+ "\u205F",
47
+ "\u3000",
48
+ ]
49
+ return s.translate({ord(space): "_" for space in spaces})
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lmnr
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: Python SDK for Laminar AI
5
5
  License: Apache-2.0
6
6
  Author: lmnr.ai
@@ -14,7 +14,9 @@ Classifier: Programming Language :: Python :: 3.12
14
14
  Requires-Dist: black (>=24.4.2,<25.0.0)
15
15
  Requires-Dist: click (>=8.1.7,<9.0.0)
16
16
  Requires-Dist: cookiecutter (>=2.6.0,<3.0.0)
17
+ Requires-Dist: lmnr-baml (>=0.40.0,<0.41.0)
17
18
  Requires-Dist: pydantic (>=2.7.4,<3.0.0)
19
+ Requires-Dist: pystache (>=0.6.5,<0.7.0)
18
20
  Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
19
21
  Requires-Dist: requests (>=2.32.3,<3.0.0)
20
22
  Requires-Dist: websockets (>=12.0,<13.0)
@@ -3,22 +3,23 @@ lmnr/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  lmnr/cli/__main__.py,sha256=8hDtWlaFZK24KhfNq_ZKgtXqYHsDQDetukOCMlsbW0Q,59
4
4
  lmnr/cli/cli.py,sha256=0Qw_dxE_N9F38asUB7pMbILJGVi-pPtqiao4aTjQQGM,2769
5
5
  lmnr/cli/parser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- lmnr/cli/parser/nodes/__init__.py,sha256=BNbbfn0WwbFDA6TNhLOaT_Ji69rCL5voUibqMD7Knng,1163
7
- lmnr/cli/parser/nodes/code.py,sha256=GXqOxN6tdiStZGWLbN3WZCmDfzwYIgSRmZ5t72AOIXc,661
6
+ lmnr/cli/parser/nodes/__init__.py,sha256=2MkPdKulb1kuNe6aT71CaqBA8iBrXyb5pq5bu_EvCb8,1052
7
+ lmnr/cli/parser/nodes/code.py,sha256=8lTPBibUzaw_t-9QoPljhxH3KA4CLn9DJjA-iWpprOA,933
8
8
  lmnr/cli/parser/nodes/condition.py,sha256=AJny0ILXbSy1hTwsRvZvDUqts9INNx63yQSkD7Dp7KU,740
9
- lmnr/cli/parser/nodes/input.py,sha256=Xwktcih7Mezqv4cEejfPkpG8uJxDsbqRytBvKmlJDYE,578
10
- lmnr/cli/parser/nodes/llm.py,sha256=iQWYFnQi5PcQD9WJpTSHbSzClM6s0wBOoEqyN5c4yQo,1674
9
+ lmnr/cli/parser/nodes/input.py,sha256=o8EfCmBbNyQL8FzmAtgnNDFlWqZmRAgkbw4HzKXZepU,539
10
+ lmnr/cli/parser/nodes/json_extractor.py,sha256=CnVwZ-wU_Ro4WkJLw9Uk_SS3yvZ66UPa5mK4JdkM8w4,723
11
+ lmnr/cli/parser/nodes/llm.py,sha256=Wpmo9cfNiYN9DRbj7oBS6RYcKXLwlGtF6RdF4jFQm5I,1866
11
12
  lmnr/cli/parser/nodes/output.py,sha256=1XBppSscxM01kfZhE9oOh2GgdCVzyPVe2RAxLI5HmUc,665
12
13
  lmnr/cli/parser/nodes/router.py,sha256=dmCx4ho8_GdFJXQa8UevMf_uEP7AKBv_MJ2zpLC6Vck,894
13
14
  lmnr/cli/parser/nodes/semantic_search.py,sha256=DWDPpV78XZ7vPIaPd86FbeDFAnKah4e61M1TOzwnt84,1352
14
- lmnr/cli/parser/nodes/types.py,sha256=XJQUg5VTbpc8LKN3-DMZkDMDiweGtKlA05edTcMYbr0,5257
15
- lmnr/cli/parser/parser.py,sha256=kAZEeg358lyj_Q1IIhQB_bA7LW3Aw6RduShIfUSmLqQ,2173
16
- lmnr/cli/parser/utils.py,sha256=wVaqHVOR9VXl8Og9nkVyCVgHIcgbtYGkDOEGPtmjZ8g,715
15
+ lmnr/cli/parser/nodes/types.py,sha256=OVXj-iMEDY9nPKCX1-zddtoszZcUL3CXYYryI7O3et0,6094
16
+ lmnr/cli/parser/parser.py,sha256=yDa-ysAkh6si_hHU8Gw8EdtNWc4pFc5RbvgWEXGEPys,2370
17
+ lmnr/cli/parser/utils.py,sha256=1oy6BApHXOF7BTXbP8v3Oi9bwOdWZjoxDlRIOfXVxro,1169
17
18
  lmnr/sdk/endpoint.py,sha256=0HjcxMUcJz-klFZO2f5xtTaoLjcaEb8vrJ_YldTWUc8,7467
18
19
  lmnr/sdk/remote_debugger.py,sha256=tC8OywEkxzeUCAHrpfhlL-iMP_0Rqh9qC2DtoYEGJjU,5174
19
20
  lmnr/types.py,sha256=OR9xRAQ5uTTwpJTDL_e3jZqxYJWvyX96CCoxr3oo94g,2112
20
- lmnr-0.2.8.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
21
- lmnr-0.2.8.dist-info/METADATA,sha256=kfHTeQtkFVsk3JK3LopVuvfr5OQkjG0YGXyoRrrqPq4,5479
22
- lmnr-0.2.8.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
23
- lmnr-0.2.8.dist-info/entry_points.txt,sha256=Qg7ZRax4k-rcQsZ26XRYQ8YFSBiyY2PNxYfq4a6PYXI,41
24
- lmnr-0.2.8.dist-info/RECORD,,
21
+ lmnr-0.2.9.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
22
+ lmnr-0.2.9.dist-info/METADATA,sha256=80S5dQujRPHbBr-zlV4W1GouTyFybBR7z2JanLJSuo4,5564
23
+ lmnr-0.2.9.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
24
+ lmnr-0.2.9.dist-info/entry_points.txt,sha256=Qg7ZRax4k-rcQsZ26XRYQ8YFSBiyY2PNxYfq4a6PYXI,41
25
+ lmnr-0.2.9.dist-info/RECORD,,
File without changes
File without changes