codemie-sdk-python 0.1.685__tar.gz → 0.1.687__tar.gz

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.
Files changed (79) hide show
  1. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/PKG-INFO +313 -19
  2. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/README.md +311 -18
  3. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/pyproject.toml +2 -2
  4. codemie_sdk_python-0.1.687/src/codemie_sdk/builders/workflow/__init__.py +53 -0
  5. codemie_sdk_python-0.1.687/src/codemie_sdk/builders/workflow/builder.py +383 -0
  6. codemie_sdk_python-0.1.687/src/codemie_sdk/builders/workflow/definition.py +389 -0
  7. codemie_sdk_python-0.1.687/src/codemie_sdk/builders/workflow/publish.py +189 -0
  8. codemie_sdk_python-0.1.687/src/codemie_sdk/models/__init__.py +0 -0
  9. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/__init__.py +0 -0
  10. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/auth/__init__.py +0 -0
  11. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/auth/credentials.py +0 -0
  12. {codemie_sdk_python-0.1.685/src/codemie_sdk/models → codemie_sdk_python-0.1.687/src/codemie_sdk/builders}/__init__.py +0 -0
  13. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/client/__init__.py +0 -0
  14. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/client/client.py +0 -0
  15. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/exceptions.py +0 -0
  16. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/a2a.py +0 -0
  17. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/activity_event.py +0 -0
  18. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/admin.py +0 -0
  19. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/analytics.py +0 -0
  20. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/assistant.py +0 -0
  21. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/categories.py +0 -0
  22. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/common.py +0 -0
  23. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/conversation.py +0 -0
  24. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/cost_center.py +0 -0
  25. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/datasource.py +0 -0
  26. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/errors.py +0 -0
  27. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/file_operation.py +0 -0
  28. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/guardrails.py +0 -0
  29. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/integration.py +0 -0
  30. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/llm.py +0 -0
  31. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/mcp_config.py +0 -0
  32. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/mermaid.py +0 -0
  33. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/project.py +0 -0
  34. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/skill.py +0 -0
  35. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/task.py +0 -0
  36. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/user.py +0 -0
  37. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/vendor_assistant.py +0 -0
  38. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/vendor_guardrail.py +0 -0
  39. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/vendor_knowledgebase.py +0 -0
  40. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/vendor_runtime.py +0 -0
  41. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/vendor_workflow.py +0 -0
  42. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/workflow.py +0 -0
  43. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/workflow_execution_payload.py +0 -0
  44. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/workflow_state.py +0 -0
  45. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/workflow_thoughts.py +0 -0
  46. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/models/workflow_transition.py +0 -0
  47. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/a2a.py +0 -0
  48. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/activity_event.py +0 -0
  49. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/admin.py +0 -0
  50. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/analytics.py +0 -0
  51. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/assistant.py +0 -0
  52. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/categories.py +0 -0
  53. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/codemie_guardrails.py +0 -0
  54. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/conversation.py +0 -0
  55. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/cost_center.py +0 -0
  56. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/customer_config_service.py +0 -0
  57. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/datasource.py +0 -0
  58. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/dynamic_config.py +0 -0
  59. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/files.py +0 -0
  60. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/integration.py +0 -0
  61. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/llm.py +0 -0
  62. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/mcp_configs.py +0 -0
  63. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/mermaid.py +0 -0
  64. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/project.py +0 -0
  65. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/project_budget_groups.py +0 -0
  66. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/skill.py +0 -0
  67. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/task.py +0 -0
  68. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/user.py +0 -0
  69. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/vendor_assistant.py +0 -0
  70. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/vendor_guardrail.py +0 -0
  71. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/vendor_knowledgebase.py +0 -0
  72. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/vendor_runtime.py +0 -0
  73. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/vendor_workflow.py +0 -0
  74. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/webhook.py +0 -0
  75. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/workflow.py +0 -0
  76. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/workflow_execution.py +0 -0
  77. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/services/workflow_execution_state.py +0 -0
  78. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/utils/__init__.py +0 -0
  79. {codemie_sdk_python-0.1.685 → codemie_sdk_python-0.1.687}/src/codemie_sdk/utils/http.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: codemie-sdk-python
3
- Version: 0.1.685
3
+ Version: 0.1.687
4
4
  Summary: CodeMie SDK for Python
5
5
  Author: Vadym Vlasenko
6
6
  Author-email: vadym_vlasenko@epam.com
@@ -8,6 +8,7 @@ Requires-Python: >=3.12,<4.0
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.12
10
10
  Requires-Dist: pydantic (>=2.12.2,<3.0.0)
11
+ Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
11
12
  Requires-Dist: requests (>=2.31.0,<3.0.0)
12
13
  Description-Content-Type: text/markdown
13
14
 
@@ -40,6 +41,8 @@ Python SDK for CodeMie services. This SDK provides a comprehensive interface to
40
41
  - [Core Methods](#core-methods-3)
41
42
  - [Workflow Execution](#workflow-execution)
42
43
  - [Workflow Configuration](#workflow-configuration)
44
+ - [Workflow Builder](#workflow-builder)
45
+ - [Workflow Node Types](#workflow-node-types)
43
46
  - [Best Practices](#best-practices)
44
47
  - [Error Handling](#error-handling)
45
48
  - [Workflow Status Monitoring](#workflow-status-monitoring)
@@ -792,29 +795,320 @@ def verify_workflow_execution(execution_service, execution_id):
792
795
 
793
796
  Workflows support various configuration options:
794
797
 
795
- 1. **Modes**:
796
- - `SEQUENTIAL`: Tasks execute in sequence
797
- - `PARALLEL`: Tasks can execute simultaneously
798
+ 1. **Modes** (`WorkflowMode`):
799
+ - `SEQUENTIAL`: states run one after another, following each state's `next`
800
+ - `AUTONOMOUS`: the platform's older mode, deprecated on the backend
798
801
 
799
802
  2. **YAML Configuration**:
803
+
804
+ The `yaml_config` you pass to `WorkflowCreateRequest` describes the workflow's assistants and
805
+ states. This one has a single assistant that reviews a pull request:
800
806
  ```yaml
801
- name: Example Workflow
802
- description: Workflow description
803
- tasks:
804
- - name: task1
805
- type: llm
806
- config:
807
- prompt: "Your prompt here"
808
- model: "gpt-4"
809
-
810
- - name: task2
811
- type: tool
812
- config:
813
- tool_name: "your-tool"
814
- parameters:
815
- param1: "value1"
807
+ assistants:
808
+ - id: reviewer
809
+ model: gpt-4.1-mini
810
+ system_prompt: You review pull requests and list the problems you find.
811
+
812
+ states:
813
+ - id: review
814
+ assistant_id: reviewer
815
+ task: Review the pull request described in {{ user_input }}
816
+ next:
817
+ state_id: end
818
+ ```
819
+ Prefer Python over YAML? [Workflow Builder](#workflow-builder) produces this same config, and
820
+ [Workflow Node Types](#workflow-node-types) shows how to add tools, custom and transform nodes,
821
+ and sub-workflows.
822
+
823
+ #### Workflow Builder
824
+
825
+ Writing a workflow by hand means writing YAML and finding out it's wrong only when the backend
826
+ rejects it. `WorkflowBuilder` lets you build the same workflow in plain Python instead, with
827
+ autocomplete and local validation for the fields the builder itself checks (not a full mirror of
828
+ the backend's schema — the backend remains the final check). Every method call gives you back a
829
+ new builder, so it's safe to branch off and customize each copy differently without stepping on
830
+ the others.
831
+
832
+ 1. **Build a workflow**
833
+ ```python
834
+ from codemie_sdk.builders.workflow import WorkflowBuilder
835
+ from codemie_sdk.builders.workflow.definition import WorkflowNextState
836
+ from codemie_sdk.models.workflow import WorkflowMode
837
+
838
+ builder = (
839
+ WorkflowBuilder("release-notes")
840
+ .with_description("Generate release notes from merged PRs")
841
+ .with_mode(WorkflowMode.SEQUENTIAL)
842
+ .add_assistant("parser", system_prompt="Extract structured PR data.")
843
+ .add_assistant("writer", assistant_id="a1b2c3d4-...") # an existing assistant, by id
844
+ .add_state("parse", "parser", "Extract merged PRs from {{ user_input }}", WorkflowNextState(state_id="draft"))
845
+ .add_state("draft", "writer", "Write the release notes", WorkflowNextState())
846
+ )
847
+
848
+ definition = builder.render()
849
+ yaml_config = definition.to_yaml_config()
850
+ ```
851
+
852
+ 2. **Reference an assistant by name instead of id**
853
+ ```python
854
+ builder_with_ref = builder.add_assistant_ref("reviewer", assistant_name="Release Notes Reviewer")
855
+
856
+ # Works once you tell it how to look the name up:
857
+ builder_with_ref.render().to_yaml_config(resolve_assistant_name=lambda name: my_lookup(name))
858
+
859
+ # Skip the resolver and it just tells you which name it couldn't find, instead of guessing.
860
+ builder_with_ref.render().to_yaml_config() # raises: unresolved assistant reference: 'Release Notes Reviewer'
861
+ ```
862
+
863
+ 3. **Branches, loops, retries, and human checkpoints**
864
+ ```python
865
+ from codemie_sdk.builders.workflow.definition import (
866
+ WorkflowRetryPolicy,
867
+ WorkflowStateCondition,
868
+ WorkflowStateSwitch,
869
+ WorkflowStateSwitchCondition,
870
+ )
871
+
872
+ # Every add_state() call returns a new builder — keep reassigning it, or the
873
+ # call is a no-op and chaining further calls on the old builder re-adds the
874
+ # same id and raises "duplicate state id".
875
+
876
+ # if / else
877
+ builder = builder.add_state("check_count", "writer", "Assemble the notes", WorkflowNextState(
878
+ condition=WorkflowStateCondition(expression="count > 0", then="publish", otherwise="end")
879
+ ))
880
+
881
+ # switch / case
882
+ builder = builder.add_state("route", "writer", "Route by language", WorkflowNextState(
883
+ switch=WorkflowStateSwitch(
884
+ cases=[
885
+ WorkflowStateSwitchCondition(condition="language == 'Java'", state_id="java_path"),
886
+ WorkflowStateSwitchCondition(condition="language == 'Python'", state_id="python_path"),
887
+ ],
888
+ default="end",
889
+ )
890
+ ))
891
+
892
+ # loop over an array, collect the results
893
+ builder = builder.add_state("summarize", "writer", "Write a line for {{ iter_item }}",
894
+ WorkflowNextState(state_id="route", iter_key="prs", output_key="lines"))
895
+
896
+ # retry on failure
897
+ builder = builder.add_state("retry_demo", "writer", "Write the release notes", WorkflowNextState(),
898
+ retry_policy=WorkflowRetryPolicy(initial_interval=1.0, backoff_factor=2.0,
899
+ max_interval=10.0, max_attempts=3))
900
+
901
+ # pause here for a human to review before continuing
902
+ builder = builder.add_state("human_review", "writer", "Assemble the notes", WorkflowNextState(), interrupt_before=True)
903
+
904
+ builder = builder.with_mode(WorkflowMode.AUTONOMOUS)
816
905
  ```
817
906
 
907
+ 4. **Publish it**
908
+ ```python
909
+ # Create a new workflow
910
+ result = builder.publish(client, project="my-project")
911
+
912
+ # Or update one that already exists
913
+ result = builder.publish(client, project="my-project", workflow_id="workflow-id")
914
+ ```
915
+ This is just a shortcut for rendering the YAML and calling `client.workflows.create_workflow`/
916
+ `.update` yourself, so nothing about using those directly changes.
917
+
918
+ 5. **Using `WorkflowBuilder` with `WorkflowCreateRequest`**
919
+
920
+ If you'd rather build the request yourself instead of publishing right away:
921
+ ```python
922
+ from codemie_sdk.models.workflow import WorkflowCreateRequest
923
+
924
+ definition = builder.render()
925
+ request = WorkflowCreateRequest(
926
+ project="my-project",
927
+ name=definition.name,
928
+ description=definition.description,
929
+ yaml_config=definition.to_yaml_config(),
930
+ mode=definition.mode,
931
+ shared=definition.shared,
932
+ icon_url=definition.icon_url,
933
+ )
934
+ ```
935
+
936
+ #### Workflow Node Types
937
+
938
+ Every state in a workflow runs one thing: an assistant, a tool, a custom node (such as
939
+ transform), or another workflow. The examples below are independent — each builds its own small
940
+ workflow.
941
+
942
+ **Assistant node**
943
+
944
+ 1. **Give it a tool**
945
+
946
+ Tool names are the ones returned by `client.assistants.get_tools()`.
947
+ ```python
948
+ from codemie_sdk.builders.workflow import (
949
+ WorkflowAssistantTool,
950
+ WorkflowBuilder,
951
+ WorkflowMCPServer,
952
+ WorkflowNextState,
953
+ )
954
+
955
+ builder = (
956
+ WorkflowBuilder("code-helper")
957
+ .add_assistant(
958
+ "helper",
959
+ system_prompt="You answer questions about the project files.",
960
+ tools=[WorkflowAssistantTool(name="_list_directory")],
961
+ )
962
+ .add_state("ask", "helper", "List what is in the project", WorkflowNextState())
963
+ )
964
+ ```
965
+
966
+ 2. **Give it an external tool**
967
+
968
+ Tools that talk to an outside system need an integration (see [Integration Service](#integration-service)).
969
+ Point to it by alias:
970
+ ```python
971
+ builder = (
972
+ WorkflowBuilder("file-reader")
973
+ .add_assistant(
974
+ "reader",
975
+ system_prompt="You read files from the project.",
976
+ tools=[WorkflowAssistantTool(name="_read_file", integration_alias="my-plugin-integration")],
977
+ )
978
+ .add_state("read", "reader", "Read the README", WorkflowNextState())
979
+ )
980
+ ```
981
+
982
+ 3. **Give it an MCP server**
983
+ ```python
984
+ builder = (
985
+ WorkflowBuilder("file-helper")
986
+ .add_assistant(
987
+ "files",
988
+ system_prompt="You manage files in the project folder.",
989
+ mcp_servers=[
990
+ WorkflowMCPServer(
991
+ name="filesystem",
992
+ config={"command": "mcp-server-filesystem", "args": ["/home/codemie"]},
993
+ ),
994
+ # or reuse a server from the MCP catalog
995
+ WorkflowMCPServer(name="github", mcp_config_id="your-catalog-config-id"),
996
+ ],
997
+ )
998
+ .add_state("tidy", "files", "Tidy up the project folder", WorkflowNextState())
999
+ )
1000
+ ```
1001
+
1002
+ **Tool, custom, transform and sub-workflow nodes**
1003
+
1004
+ Tools and custom nodes are declared once and then used by id, the same way an assistant is
1005
+ registered before a state runs it. Naming follows the workflow YAML, so `tool_id` and
1006
+ `custom_node_id` in a state point at the `id` you declared.
1007
+
1008
+ 4. **Tool node** — runs a single tool directly, no LLM in between
1009
+ ```python
1010
+ builder = (
1011
+ WorkflowBuilder("find-sources")
1012
+ .add_tool(
1013
+ "find_sources",
1014
+ "_search_files_by_regex",
1015
+ integration_alias="my-plugin-integration",
1016
+ )
1017
+ .add_tool_state(
1018
+ "search",
1019
+ "find_sources",
1020
+ WorkflowNextState(state_id="end"),
1021
+ tool_args={"path": "src", "regex": "[.]py$"},
1022
+ )
1023
+ )
1024
+ ```
1025
+ A tool that is served by an MCP server takes `mcp_server`:
1026
+ ```python
1027
+ builder = WorkflowBuilder("run-commands").add_tool(
1028
+ "run_command",
1029
+ "run_command",
1030
+ mcp_server=WorkflowMCPServer(
1031
+ name="CLI MCP Server",
1032
+ description="Runs shell commands",
1033
+ mcp_connect_url="http://codemie-mcp-connect:3000",
1034
+ config={"command": "uvx", "args": ["cli-mcp-server"]},
1035
+ ),
1036
+ )
1037
+ ```
1038
+
1039
+ 5. **Custom node** — a ready-made processing step from the platform. The first argument is your
1040
+ name for it, the second is the kind of node
1041
+ ```python
1042
+ builder = (
1043
+ WorkflowBuilder("review-summary")
1044
+ .add_custom_node(
1045
+ "summary",
1046
+ "state_processor_node",
1047
+ model="gpt-4.1-mini",
1048
+ config={
1049
+ "state_id": "review",
1050
+ "output_template": "{% for item in items %}- {{ item.file_path }}: {{ item.conclusion }}\n{% endfor %}",
1051
+ },
1052
+ )
1053
+ .add_custom_node_state(
1054
+ "summarize",
1055
+ "summary",
1056
+ WorkflowNextState(state_id="end"),
1057
+ task="Summarize the review results",
1058
+ )
1059
+ )
1060
+ ```
1061
+
1062
+ 6. **Transform node** — reshapes data without an LLM. It's a custom node, so you run it with
1063
+ `add_custom_node_state`
1064
+ ```python
1065
+ builder = (
1066
+ WorkflowBuilder("shape-pr")
1067
+ .add_transform_node(
1068
+ "shape_pr",
1069
+ mappings=[
1070
+ {"output_field": "title", "type": "extract", "source_path": "pull_request.title"},
1071
+ {
1072
+ "output_field": "summary",
1073
+ "type": "template",
1074
+ "template": "PR #{{ number }} by @{{ pull_request.user.login }}",
1075
+ },
1076
+ {
1077
+ "output_field": "actionable",
1078
+ "type": "condition",
1079
+ "condition": "action in ['opened', 'reopened']",
1080
+ "then_value": True,
1081
+ "else_value": False,
1082
+ },
1083
+ ],
1084
+ on_error="fail",
1085
+ )
1086
+ .add_custom_node_state(
1087
+ "shape_event",
1088
+ "shape_pr",
1089
+ WorkflowNextState(state_id="end", output_key="pr"),
1090
+ task="Shape the PR event",
1091
+ )
1092
+ )
1093
+ ```
1094
+ Other mapping types are `constant`, `script` and `array_map`. `on_error` can be `fail`, `skip`,
1095
+ `default` (use together with `default_output`) or `partial`.
1096
+
1097
+ 7. **Sub-workflow node** — runs another workflow and waits for it
1098
+ ```python
1099
+ builder = WorkflowBuilder("review-pipeline").add_sub_workflow_state(
1100
+ "run_review",
1101
+ "your-workflow-id",
1102
+ WorkflowNextState(state_id="end"),
1103
+ )
1104
+ ```
1105
+ The sub-workflow gets the previous state's output as its input, and its last output becomes this
1106
+ state's output. The `subWorkflow` feature has to be enabled on your CodeMie instance, and
1107
+ workflows can only be nested up to a limit.
1108
+
1109
+ All of these chain on the same builder, so one workflow can mix assistants, tools, custom
1110
+ nodes and sub-workflows.
1111
+
818
1112
  #### Best Practices
819
1113
 
820
1114
  1. **Workflow Design**: