graphcore-studio 0.2.0__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.
- graphcore_studio-0.2.0/.github/workflows/ci.yml +51 -0
- graphcore_studio-0.2.0/.github/workflows/wheels.yml +84 -0
- graphcore_studio-0.2.0/.gitignore +13 -0
- graphcore_studio-0.2.0/CMakeLists.txt +55 -0
- graphcore_studio-0.2.0/LICENSE +21 -0
- graphcore_studio-0.2.0/PKG-INFO +171 -0
- graphcore_studio-0.2.0/README.md +145 -0
- graphcore_studio-0.2.0/cmake/GraphCoreConfig.cmake.in +2 -0
- graphcore_studio-0.2.0/docs/ARCHITECTURE.md +52 -0
- graphcore_studio-0.2.0/docs/PYPI_RELEASE.md +37 -0
- graphcore_studio-0.2.0/docs/ROADMAP.md +24 -0
- graphcore_studio-0.2.0/docs/SEMANTICS.md +48 -0
- graphcore_studio-0.2.0/docs/VALIDATION.md +16 -0
- graphcore_studio-0.2.0/examples/approval.cpp +35 -0
- graphcore_studio-0.2.0/examples/complex_deepseek_workflow.json +371 -0
- graphcore_studio-0.2.0/examples/langgraph_deepseek_workflow_notebook.py +219 -0
- graphcore_studio-0.2.0/examples/python_agent.py +30 -0
- graphcore_studio-0.2.0/include/graphcore/c_api.h +51 -0
- graphcore_studio-0.2.0/include/graphcore/graphcore.hpp +83 -0
- graphcore_studio-0.2.0/pyproject.toml +74 -0
- graphcore_studio-0.2.0/python/graphcore/__init__.py +303 -0
- graphcore_studio-0.2.0/requirements-models.txt +2 -0
- graphcore_studio-0.2.0/requirements-pydantic.txt +1 -0
- graphcore_studio-0.2.0/run_studio.py +38 -0
- graphcore_studio-0.2.0/scripts/build.sh +16 -0
- graphcore_studio-0.2.0/src/c_api.cpp +103 -0
- graphcore_studio-0.2.0/src/graphcore.cpp +161 -0
- graphcore_studio-0.2.0/studio/__init__.py +1 -0
- graphcore_studio-0.2.0/studio/cli.py +9 -0
- graphcore_studio-0.2.0/studio/engine.py +338 -0
- graphcore_studio-0.2.0/studio/example_plugin.py +8 -0
- graphcore_studio-0.2.0/studio/server.py +366 -0
- graphcore_studio-0.2.0/studio/templates/01-research.json +114 -0
- graphcore_studio-0.2.0/studio/templates/02-structured.json +86 -0
- graphcore_studio-0.2.0/studio/templates/03-tools.json +70 -0
- graphcore_studio-0.2.0/studio/templates/04-team.json +117 -0
- graphcore_studio-0.2.0/studio/templates/05-model.json +19 -0
- graphcore_studio-0.2.0/studio/web/app.js +108 -0
- graphcore_studio-0.2.0/studio/web/index.html +43 -0
- graphcore_studio-0.2.0/studio/web/style.css +9 -0
- graphcore_studio-0.2.0/tests/core_tests.cpp +61 -0
- graphcore_studio-0.2.0/tests/test_python.py +129 -0
- graphcore_studio-0.2.0/tests/test_studio.py +216 -0
- graphcore_studio-0.2.0/tests/test_studio_http.py +104 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: build-and-test
|
|
2
|
+
on: [push, pull_request]
|
|
3
|
+
jobs:
|
|
4
|
+
packaging:
|
|
5
|
+
runs-on: ubuntu-latest
|
|
6
|
+
steps:
|
|
7
|
+
- uses: actions/checkout@v4
|
|
8
|
+
- uses: actions/setup-python@v5
|
|
9
|
+
with:
|
|
10
|
+
python-version: '3.12'
|
|
11
|
+
- run: python -m pip install build twine
|
|
12
|
+
- run: python -m build --sdist --wheel
|
|
13
|
+
- run: python -m twine check dist/*.whl dist/*.tar.gz
|
|
14
|
+
- name: Install the wheel and exercise the native runtime
|
|
15
|
+
shell: bash
|
|
16
|
+
run: |
|
|
17
|
+
python -m pip install --no-deps --target "$RUNNER_TEMP/graphcore-installed" dist/*.whl
|
|
18
|
+
cd "$RUNNER_TEMP"
|
|
19
|
+
PYTHONPATH="$RUNNER_TEMP/graphcore-installed" python - <<'PY'
|
|
20
|
+
from graphcore import END, Graph
|
|
21
|
+
from studio.server import TEMPLATES, WEB
|
|
22
|
+
assert (WEB / "index.html").is_file()
|
|
23
|
+
assert len(list(TEMPLATES.glob("*.json"))) == 5
|
|
24
|
+
with Graph() as graph:
|
|
25
|
+
graph.add_node("check", lambda state, context: {"ok": True})
|
|
26
|
+
graph.set_entry("check").add_edge("check", END)
|
|
27
|
+
assert graph.invoke().state["ok"] is True
|
|
28
|
+
PY
|
|
29
|
+
test:
|
|
30
|
+
strategy:
|
|
31
|
+
fail-fast: false
|
|
32
|
+
matrix:
|
|
33
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
34
|
+
python: ['3.9', '3.12']
|
|
35
|
+
runs-on: ${{ matrix.os }}
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v4
|
|
38
|
+
- uses: actions/setup-python@v5
|
|
39
|
+
with:
|
|
40
|
+
python-version: ${{ matrix.python }}
|
|
41
|
+
- run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
|
42
|
+
- run: cmake --build build --config Release
|
|
43
|
+
- run: ctest --test-dir build -C Release --output-on-failure
|
|
44
|
+
sanitizers:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v4
|
|
48
|
+
- run: >-
|
|
49
|
+
c++ -std=c++17 -g -fsanitize=address,undefined -fno-omit-frame-pointer
|
|
50
|
+
-Iinclude src/graphcore.cpp tests/core_tests.cpp -o core_tests
|
|
51
|
+
- run: ./core_tests
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
name: build-release-artifacts
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
tags:
|
|
7
|
+
- 'v*'
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
wheels:
|
|
14
|
+
name: wheels (${{ matrix.runner }})
|
|
15
|
+
runs-on: ${{ matrix.runner }}
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: false
|
|
18
|
+
matrix:
|
|
19
|
+
runner:
|
|
20
|
+
- ubuntu-latest
|
|
21
|
+
- ubuntu-24.04-arm
|
|
22
|
+
- windows-latest
|
|
23
|
+
- macos-15-intel
|
|
24
|
+
- macos-14
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: '3.12'
|
|
30
|
+
- name: Build wheels for supported CPython versions
|
|
31
|
+
run: python -m pip install cibuildwheel && python -m cibuildwheel --output-dir wheelhouse
|
|
32
|
+
env:
|
|
33
|
+
CIBW_BUILD: 'cp39-* cp310-* cp311-* cp312-* cp313-*'
|
|
34
|
+
CIBW_SKIP: '*-musllinux_*'
|
|
35
|
+
CIBW_TEST_COMMAND: >-
|
|
36
|
+
python -c "from graphcore import END, Graph; g=Graph(); g.add_node('probe', lambda s,c: {'ok': True}); g.set_entry('probe').add_edge('probe', END); assert g.invoke().state['ok']; g.close()"
|
|
37
|
+
CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET=11.0
|
|
38
|
+
CIBW_BUILD_VERBOSITY: '1'
|
|
39
|
+
- uses: actions/upload-artifact@v4
|
|
40
|
+
with:
|
|
41
|
+
name: package-wheels-${{ matrix.runner }}
|
|
42
|
+
path: wheelhouse/*.whl
|
|
43
|
+
if-no-files-found: error
|
|
44
|
+
retention-days: 14
|
|
45
|
+
|
|
46
|
+
sdist:
|
|
47
|
+
name: source distribution
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@v4
|
|
51
|
+
- uses: actions/setup-python@v5
|
|
52
|
+
with:
|
|
53
|
+
python-version: '3.12'
|
|
54
|
+
- run: python -m pip install build twine
|
|
55
|
+
- run: python -m build --sdist --outdir dist
|
|
56
|
+
- run: python -m twine check dist/*.tar.gz
|
|
57
|
+
- uses: actions/upload-artifact@v4
|
|
58
|
+
with:
|
|
59
|
+
name: package-sdist
|
|
60
|
+
path: dist/*.tar.gz
|
|
61
|
+
if-no-files-found: error
|
|
62
|
+
retention-days: 14
|
|
63
|
+
|
|
64
|
+
collect:
|
|
65
|
+
name: collect and check release files
|
|
66
|
+
needs: [wheels, sdist]
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
steps:
|
|
69
|
+
- uses: actions/setup-python@v5
|
|
70
|
+
with:
|
|
71
|
+
python-version: '3.12'
|
|
72
|
+
- run: python -m pip install twine
|
|
73
|
+
- uses: actions/download-artifact@v4
|
|
74
|
+
with:
|
|
75
|
+
pattern: package-*
|
|
76
|
+
path: dist
|
|
77
|
+
merge-multiple: true
|
|
78
|
+
- run: python -m twine check dist/*.whl dist/*.tar.gz
|
|
79
|
+
- uses: actions/upload-artifact@v4
|
|
80
|
+
with:
|
|
81
|
+
name: graphcore-studio-distributions
|
|
82
|
+
path: dist/*
|
|
83
|
+
if-no-files-found: error
|
|
84
|
+
retention-days: 30
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.16)
|
|
2
|
+
project(GraphCore VERSION 0.2.0 LANGUAGES CXX)
|
|
3
|
+
option(GRAPHCORE_BUILD_TESTS "Build tests" ON)
|
|
4
|
+
option(GRAPHCORE_BUILD_EXAMPLES "Build examples" ON)
|
|
5
|
+
option(GRAPHCORE_BUILD_PYTHON "Build C ABI shared library for Python" ON)
|
|
6
|
+
set(GRAPHCORE_PYTHON_PACKAGE_DIR "" CACHE STRING "Install the shared library inside this Python package directory")
|
|
7
|
+
add_library(graphcore STATIC src/graphcore.cpp)
|
|
8
|
+
add_library(GraphCore::graphcore ALIAS graphcore)
|
|
9
|
+
target_compile_features(graphcore PUBLIC cxx_std_17)
|
|
10
|
+
target_include_directories(graphcore PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>)
|
|
11
|
+
set_target_properties(graphcore PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
|
12
|
+
if(GRAPHCORE_BUILD_PYTHON)
|
|
13
|
+
add_library(graphcore_shared SHARED src/c_api.cpp)
|
|
14
|
+
target_link_libraries(graphcore_shared PRIVATE graphcore)
|
|
15
|
+
target_compile_definitions(graphcore_shared PRIVATE GRAPHCORE_BUILD_SHARED)
|
|
16
|
+
set_target_properties(graphcore_shared PROPERTIES OUTPUT_NAME graphcore ARCHIVE_OUTPUT_NAME graphcore_c)
|
|
17
|
+
endif()
|
|
18
|
+
if(GRAPHCORE_BUILD_EXAMPLES)
|
|
19
|
+
add_executable(graphcore_demo examples/approval.cpp)
|
|
20
|
+
target_link_libraries(graphcore_demo PRIVATE graphcore)
|
|
21
|
+
endif()
|
|
22
|
+
if(GRAPHCORE_BUILD_TESTS)
|
|
23
|
+
enable_testing()
|
|
24
|
+
add_executable(graphcore_tests tests/core_tests.cpp)
|
|
25
|
+
target_link_libraries(graphcore_tests PRIVATE graphcore)
|
|
26
|
+
add_test(NAME core COMMAND graphcore_tests)
|
|
27
|
+
if(GRAPHCORE_BUILD_PYTHON)
|
|
28
|
+
find_package(Python3 COMPONENTS Interpreter QUIET)
|
|
29
|
+
if(Python3_FOUND)
|
|
30
|
+
add_test(NAME python COMMAND ${CMAKE_COMMAND} -E env
|
|
31
|
+
"PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}/python"
|
|
32
|
+
"GRAPHCORE_LIBRARY=$<TARGET_FILE:graphcore_shared>"
|
|
33
|
+
${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_python.py)
|
|
34
|
+
endif()
|
|
35
|
+
endif()
|
|
36
|
+
endif()
|
|
37
|
+
include(GNUInstallDirs)
|
|
38
|
+
install(TARGETS graphcore EXPORT GraphCoreTargets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
|
39
|
+
if(GRAPHCORE_BUILD_PYTHON)
|
|
40
|
+
if(GRAPHCORE_PYTHON_PACKAGE_DIR)
|
|
41
|
+
set(GRAPHCORE_SHARED_INSTALL_DIR ${GRAPHCORE_PYTHON_PACKAGE_DIR})
|
|
42
|
+
else()
|
|
43
|
+
set(GRAPHCORE_SHARED_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR})
|
|
44
|
+
endif()
|
|
45
|
+
install(TARGETS graphcore_shared
|
|
46
|
+
LIBRARY DESTINATION ${GRAPHCORE_SHARED_INSTALL_DIR}
|
|
47
|
+
RUNTIME DESTINATION ${GRAPHCORE_SHARED_INSTALL_DIR}
|
|
48
|
+
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
|
49
|
+
endif()
|
|
50
|
+
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
|
51
|
+
install(EXPORT GraphCoreTargets NAMESPACE GraphCore:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/GraphCore)
|
|
52
|
+
include(CMakePackageConfigHelpers)
|
|
53
|
+
configure_package_config_file(cmake/GraphCoreConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/GraphCoreConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/GraphCore)
|
|
54
|
+
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/GraphCoreConfigVersion.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion)
|
|
55
|
+
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/GraphCoreConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/GraphCoreConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/GraphCore)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GraphCore contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: graphcore-studio
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A local-first visual agent workflow studio powered by a native C++ graph runtime
|
|
5
|
+
Keywords: agents,agentic-workflows,graph,C++,visual-workflow-builder
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: C++
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Provides-Extra: models
|
|
22
|
+
Requires-Dist: openrouter<1,>=0.10; extra == "models"
|
|
23
|
+
Provides-Extra: pydantic
|
|
24
|
+
Requires-Dist: pydantic<3,>=2; extra == "pydantic"
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# GraphCore Studio
|
|
28
|
+
|
|
29
|
+
**Design local agent workflows on a canvas. Run them with a C++ engine. Extend them with Python.**
|
|
30
|
+
|
|
31
|
+
GraphCore Studio is an interactive, local-first visual agent framework. Drag nodes onto a canvas, connect agent handoffs, add conditional branches, validate structured output, call Python tools, and pause for human approval. Save and run workflows on your own machine.
|
|
32
|
+
|
|
33
|
+
## Start Studio
|
|
34
|
+
|
|
35
|
+
Requirements: Python 3.9+ and a C++17 compiler. You do not need CMake, Node.js, Docker, API keys, an internet connection, or a model to try the included workflows.
|
|
36
|
+
|
|
37
|
+
From the project folder, run one command:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
python3 run_studio.py
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Open [http://127.0.0.1:8787](http://127.0.0.1:8787). The launcher compiles the C++ shared library on first run, then opens a server accessible only from your machine. On Windows, use PowerShell in a Visual Studio Developer Terminal with the C++ workload installed:
|
|
44
|
+
|
|
45
|
+
```powershell
|
|
46
|
+
py run_studio.py
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
If Python was installed through the Windows Store and `py` is unavailable, run `python run_studio.py` from the Visual Studio Developer Terminal.
|
|
50
|
+
|
|
51
|
+
Stop the server with Ctrl+C. Workflows and run history live in `studio/data/`; drafts are saved in this browser. Run `python3 run_studio.py --help` to configure the port, data directory, or Python tool plugins.
|
|
52
|
+
|
|
53
|
+
### Install the PyPI package
|
|
54
|
+
|
|
55
|
+
After the first release is published, install the prebuilt package with:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
python -m pip install "graphcore-studio[models,pydantic]"
|
|
59
|
+
graphcore-studio
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The wheel bundles the C++ runtime and Studio UI. Source installs build the native runtime and therefore require a C++17 compiler and CMake. The distribution is named `graphcore-studio`; its Python API imports as `graphcore`.
|
|
63
|
+
|
|
64
|
+
### Use Pydantic
|
|
65
|
+
|
|
66
|
+
The built-in structured-output validator works offline with no extra packages. To enable optional Pydantic v2 validation:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
python3 -m pip install -r requirements-pydantic.txt
|
|
70
|
+
python3 run_studio.py
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
On Windows, replace `python3` with `py -3`. Restart Studio after installing packages. The Structured output node shows whether Pydantic is available.
|
|
74
|
+
|
|
75
|
+
### Use OpenRouter models
|
|
76
|
+
|
|
77
|
+
The **Chat Model** and **Agent** nodes use GraphCore's model adapter with the official OpenRouter Python SDK. C++ owns graph scheduling, state commits, conditions, and checkpoints; the Python wrapper calls the SDK. The OpenRouter request runs through the project-owned adapter rather than a chain framework.
|
|
78
|
+
|
|
79
|
+
Install the SDK into the Python environment used to run Studio:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
python3 -m pip install -r requirements-models.txt
|
|
83
|
+
python3 run_studio.py
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
For a PyPI installation, use the `models` extra instead: `python -m pip install "graphcore-studio[models]"`.
|
|
87
|
+
|
|
88
|
+
In Studio, open **Model integrations**, add `OPENROUTER_API_KEY`, and save. Select **OpenRouter** on a Chat Model node and enter a model ID from the [OpenRouter model catalog](https://openrouter.ai/models). Compose system/user/assistant messages and use **Insert variable** to reference workflow inputs and prior node outputs. Studio loads the local `.env` file automatically; secret values are not included in workflow files. The offline fixture needs no SDK, key, or network call.
|
|
89
|
+
|
|
90
|
+
## Build a workflow
|
|
91
|
+
|
|
92
|
+
1. Pick a template or drag a node from the left panel.
|
|
93
|
+
2. Connect the output dots to the next node's input. Conditional nodes have separate true and false outputs.
|
|
94
|
+
3. Select nodes to edit prompts, tools, schemas, and routing rules.
|
|
95
|
+
4. Give the workflow JSON input in the panel under the canvas.
|
|
96
|
+
5. Run it and inspect the output, state, and per-node trace.
|
|
97
|
+
6. Save locally, export the workflow JSON, or approve a saved human checkpoint.
|
|
98
|
+
|
|
99
|
+
The starter templates cover research and review, structured output, Python tools, a supervisor delegating work to specialist agents, and a chat-model playground. The canvas also supports node search, keyboard undo/redo, zoom, pan, layout changes, validation, and run history.
|
|
100
|
+
|
|
101
|
+
Prompt templates reference state with `{{input}}`, `{{research}}`, or nested fields such as `{{record.summary}}`. Structured Output defines required fields and types. Python plugins extend the node's registered-tool list without placing executable source code in the workflow document.
|
|
102
|
+
|
|
103
|
+
## Register a Python library or tool
|
|
104
|
+
|
|
105
|
+
A plugin is a local, trusted Python module. Studio loads it when the server starts. Import a library in your tool and register an ordinary function:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
def register(add_tool):
|
|
109
|
+
from my_library import Client
|
|
110
|
+
client = Client()
|
|
111
|
+
|
|
112
|
+
def search(value, config):
|
|
113
|
+
return {"matches": client.search(str(value))}
|
|
114
|
+
|
|
115
|
+
add_tool("search", search, "Search our knowledge base")
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Start Studio with `python3 run_studio.py --plugin path/to/plugin.py`, then select the registered function in a Python Tool node. Plugins execute as the current user; load only modules you trust. The included `studio/example_plugin.py` shows a small plugin.
|
|
119
|
+
|
|
120
|
+
## What runs where?
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
Visual editor & local HTTP API
|
|
124
|
+
│ workflow JSON and node registrations
|
|
125
|
+
Python integrations ───── OpenRouter SDK / Pydantic / your packages
|
|
126
|
+
│ Python C ABI
|
|
127
|
+
C++17 graph scheduler ─── conditions / routes / checkpoints
|
|
128
|
+
│
|
|
129
|
+
Local workflow & run files
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The C++ runtime owns graph validation at compile time, step scheduling, native equality conditions, and checkpoint transitions. Python supplies model and tool integrations and the local editor server. Model and Python tool code executes in Python; agent handoffs are sequential. Performance benefits depend on the workload. No blanket speedup claim is made.
|
|
133
|
+
|
|
134
|
+
## Build or embed the C++ core
|
|
135
|
+
|
|
136
|
+
The editor launcher builds the native shared library automatically. To build the core tests and examples as well:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
./scripts/build.sh
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
For CMake users with CMake 3.16 or newer:
|
|
143
|
+
|
|
144
|
+
```sh
|
|
145
|
+
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
|
146
|
+
cmake --build build --config Release
|
|
147
|
+
ctest --test-dir build -C Release --output-on-failure
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Compile a C++ example directly with a compiler:
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
c++ -std=c++17 -O2 -Iinclude src/graphcore.cpp examples/approval.cpp -o graphcore_demo
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The C++ interface is in `include/graphcore/graphcore.hpp`; the experimental C ABI used by Python is in `include/graphcore/c_api.h`.
|
|
157
|
+
|
|
158
|
+
## Scope and guarantees
|
|
159
|
+
|
|
160
|
+
This is an early, working local framework. The UI has runnable templates and a native backend. The runtime currently executes one node at a time. Retries can repeat side effects; use idempotency keys in external systems. File checkpoints support local process-restart recovery but do not promise power-loss durability, multiple writers, encryption, or database transactions. Python async cancellation is cooperative only between callbacks. See the [execution contract](docs/SEMANTICS.md).
|
|
161
|
+
|
|
162
|
+
Parallel fan-out and joins, database-backed persistence, distributed worker coordination, and an authenticated server remain future work. A release workflow now builds CPython wheels for Linux, macOS, and Windows; those hosted builds still need to run before the wheels are verified for release. The local server uses a session token and origin checks, and binds only to loopback; it is not designed for public network exposure.
|
|
163
|
+
|
|
164
|
+
## Project notes
|
|
165
|
+
|
|
166
|
+
- [Architecture and extension points](docs/ARCHITECTURE.md)
|
|
167
|
+
- [Local UI and runtime semantics](docs/SEMANTICS.md)
|
|
168
|
+
- [Roadmap](docs/ROADMAP.md)
|
|
169
|
+
- [Local verification record](docs/VALIDATION.md)
|
|
170
|
+
- [PyPI release checklist](docs/PYPI_RELEASE.md)
|
|
171
|
+
- [Example plugin](studio/example_plugin.py)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# GraphCore Studio
|
|
2
|
+
|
|
3
|
+
**Design local agent workflows on a canvas. Run them with a C++ engine. Extend them with Python.**
|
|
4
|
+
|
|
5
|
+
GraphCore Studio is an interactive, local-first visual agent framework. Drag nodes onto a canvas, connect agent handoffs, add conditional branches, validate structured output, call Python tools, and pause for human approval. Save and run workflows on your own machine.
|
|
6
|
+
|
|
7
|
+
## Start Studio
|
|
8
|
+
|
|
9
|
+
Requirements: Python 3.9+ and a C++17 compiler. You do not need CMake, Node.js, Docker, API keys, an internet connection, or a model to try the included workflows.
|
|
10
|
+
|
|
11
|
+
From the project folder, run one command:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
python3 run_studio.py
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Open [http://127.0.0.1:8787](http://127.0.0.1:8787). The launcher compiles the C++ shared library on first run, then opens a server accessible only from your machine. On Windows, use PowerShell in a Visual Studio Developer Terminal with the C++ workload installed:
|
|
18
|
+
|
|
19
|
+
```powershell
|
|
20
|
+
py run_studio.py
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
If Python was installed through the Windows Store and `py` is unavailable, run `python run_studio.py` from the Visual Studio Developer Terminal.
|
|
24
|
+
|
|
25
|
+
Stop the server with Ctrl+C. Workflows and run history live in `studio/data/`; drafts are saved in this browser. Run `python3 run_studio.py --help` to configure the port, data directory, or Python tool plugins.
|
|
26
|
+
|
|
27
|
+
### Install the PyPI package
|
|
28
|
+
|
|
29
|
+
After the first release is published, install the prebuilt package with:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
python -m pip install "graphcore-studio[models,pydantic]"
|
|
33
|
+
graphcore-studio
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The wheel bundles the C++ runtime and Studio UI. Source installs build the native runtime and therefore require a C++17 compiler and CMake. The distribution is named `graphcore-studio`; its Python API imports as `graphcore`.
|
|
37
|
+
|
|
38
|
+
### Use Pydantic
|
|
39
|
+
|
|
40
|
+
The built-in structured-output validator works offline with no extra packages. To enable optional Pydantic v2 validation:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
python3 -m pip install -r requirements-pydantic.txt
|
|
44
|
+
python3 run_studio.py
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
On Windows, replace `python3` with `py -3`. Restart Studio after installing packages. The Structured output node shows whether Pydantic is available.
|
|
48
|
+
|
|
49
|
+
### Use OpenRouter models
|
|
50
|
+
|
|
51
|
+
The **Chat Model** and **Agent** nodes use GraphCore's model adapter with the official OpenRouter Python SDK. C++ owns graph scheduling, state commits, conditions, and checkpoints; the Python wrapper calls the SDK. The OpenRouter request runs through the project-owned adapter rather than a chain framework.
|
|
52
|
+
|
|
53
|
+
Install the SDK into the Python environment used to run Studio:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python3 -m pip install -r requirements-models.txt
|
|
57
|
+
python3 run_studio.py
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For a PyPI installation, use the `models` extra instead: `python -m pip install "graphcore-studio[models]"`.
|
|
61
|
+
|
|
62
|
+
In Studio, open **Model integrations**, add `OPENROUTER_API_KEY`, and save. Select **OpenRouter** on a Chat Model node and enter a model ID from the [OpenRouter model catalog](https://openrouter.ai/models). Compose system/user/assistant messages and use **Insert variable** to reference workflow inputs and prior node outputs. Studio loads the local `.env` file automatically; secret values are not included in workflow files. The offline fixture needs no SDK, key, or network call.
|
|
63
|
+
|
|
64
|
+
## Build a workflow
|
|
65
|
+
|
|
66
|
+
1. Pick a template or drag a node from the left panel.
|
|
67
|
+
2. Connect the output dots to the next node's input. Conditional nodes have separate true and false outputs.
|
|
68
|
+
3. Select nodes to edit prompts, tools, schemas, and routing rules.
|
|
69
|
+
4. Give the workflow JSON input in the panel under the canvas.
|
|
70
|
+
5. Run it and inspect the output, state, and per-node trace.
|
|
71
|
+
6. Save locally, export the workflow JSON, or approve a saved human checkpoint.
|
|
72
|
+
|
|
73
|
+
The starter templates cover research and review, structured output, Python tools, a supervisor delegating work to specialist agents, and a chat-model playground. The canvas also supports node search, keyboard undo/redo, zoom, pan, layout changes, validation, and run history.
|
|
74
|
+
|
|
75
|
+
Prompt templates reference state with `{{input}}`, `{{research}}`, or nested fields such as `{{record.summary}}`. Structured Output defines required fields and types. Python plugins extend the node's registered-tool list without placing executable source code in the workflow document.
|
|
76
|
+
|
|
77
|
+
## Register a Python library or tool
|
|
78
|
+
|
|
79
|
+
A plugin is a local, trusted Python module. Studio loads it when the server starts. Import a library in your tool and register an ordinary function:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
def register(add_tool):
|
|
83
|
+
from my_library import Client
|
|
84
|
+
client = Client()
|
|
85
|
+
|
|
86
|
+
def search(value, config):
|
|
87
|
+
return {"matches": client.search(str(value))}
|
|
88
|
+
|
|
89
|
+
add_tool("search", search, "Search our knowledge base")
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Start Studio with `python3 run_studio.py --plugin path/to/plugin.py`, then select the registered function in a Python Tool node. Plugins execute as the current user; load only modules you trust. The included `studio/example_plugin.py` shows a small plugin.
|
|
93
|
+
|
|
94
|
+
## What runs where?
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
Visual editor & local HTTP API
|
|
98
|
+
│ workflow JSON and node registrations
|
|
99
|
+
Python integrations ───── OpenRouter SDK / Pydantic / your packages
|
|
100
|
+
│ Python C ABI
|
|
101
|
+
C++17 graph scheduler ─── conditions / routes / checkpoints
|
|
102
|
+
│
|
|
103
|
+
Local workflow & run files
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The C++ runtime owns graph validation at compile time, step scheduling, native equality conditions, and checkpoint transitions. Python supplies model and tool integrations and the local editor server. Model and Python tool code executes in Python; agent handoffs are sequential. Performance benefits depend on the workload. No blanket speedup claim is made.
|
|
107
|
+
|
|
108
|
+
## Build or embed the C++ core
|
|
109
|
+
|
|
110
|
+
The editor launcher builds the native shared library automatically. To build the core tests and examples as well:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
./scripts/build.sh
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
For CMake users with CMake 3.16 or newer:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
|
120
|
+
cmake --build build --config Release
|
|
121
|
+
ctest --test-dir build -C Release --output-on-failure
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Compile a C++ example directly with a compiler:
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
c++ -std=c++17 -O2 -Iinclude src/graphcore.cpp examples/approval.cpp -o graphcore_demo
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The C++ interface is in `include/graphcore/graphcore.hpp`; the experimental C ABI used by Python is in `include/graphcore/c_api.h`.
|
|
131
|
+
|
|
132
|
+
## Scope and guarantees
|
|
133
|
+
|
|
134
|
+
This is an early, working local framework. The UI has runnable templates and a native backend. The runtime currently executes one node at a time. Retries can repeat side effects; use idempotency keys in external systems. File checkpoints support local process-restart recovery but do not promise power-loss durability, multiple writers, encryption, or database transactions. Python async cancellation is cooperative only between callbacks. See the [execution contract](docs/SEMANTICS.md).
|
|
135
|
+
|
|
136
|
+
Parallel fan-out and joins, database-backed persistence, distributed worker coordination, and an authenticated server remain future work. A release workflow now builds CPython wheels for Linux, macOS, and Windows; those hosted builds still need to run before the wheels are verified for release. The local server uses a session token and origin checks, and binds only to loopback; it is not designed for public network exposure.
|
|
137
|
+
|
|
138
|
+
## Project notes
|
|
139
|
+
|
|
140
|
+
- [Architecture and extension points](docs/ARCHITECTURE.md)
|
|
141
|
+
- [Local UI and runtime semantics](docs/SEMANTICS.md)
|
|
142
|
+
- [Roadmap](docs/ROADMAP.md)
|
|
143
|
+
- [Local verification record](docs/VALIDATION.md)
|
|
144
|
+
- [PyPI release checklist](docs/PYPI_RELEASE.md)
|
|
145
|
+
- [Example plugin](studio/example_plugin.py)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
```text
|
|
4
|
+
Browser visual canvas (plain HTML/CSS/JS)
|
|
5
|
+
│ local JSON API with per-launch token
|
|
6
|
+
Python standard-library HTTP server and run manager
|
|
7
|
+
├── registered Python / optional Pydantic nodes
|
|
8
|
+
├── official OpenRouter Python SDK adapter
|
|
9
|
+
└── ctypes C ABI bridge
|
|
10
|
+
│
|
|
11
|
+
C++17 GraphCore runtime
|
|
12
|
+
├── workflow compiler and node routing
|
|
13
|
+
├── equality condition node
|
|
14
|
+
└── crash-recoverable local checkpoint files
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Source map
|
|
18
|
+
|
|
19
|
+
- `studio/web/`: dependency-free visual editor; editing, drag-to-place, routes, inspector, templates, history and export.
|
|
20
|
+
- `studio/server.py`: loopback-only HTTP API, plugin registration, run workers, workflow/run storage and crash recovery.
|
|
21
|
+
- `studio/engine.py`: workflow validation, Python/Pydantic integrations, OpenRouter SDK adapter and C++ graph compilation.
|
|
22
|
+
- `studio/templates/`: four runnable design examples.
|
|
23
|
+
- `include/graphcore/graphcore.hpp`, `src/graphcore.cpp`: C++ API, sequential scheduler and checkpoint store.
|
|
24
|
+
- `include/graphcore/c_api.h`, `src/c_api.cpp`: C ABI used by the Python runtime.
|
|
25
|
+
- `python/graphcore/`: Python SDK and asyncio callback bridge.
|
|
26
|
+
- `run_studio.py`: one-command native compilation and local startup.
|
|
27
|
+
|
|
28
|
+
No Node.js, frontend bundler, database, C++ package manager or Python web framework is required. The OpenRouter Python SDK is optional and required only for live model calls; the offline fixture works with the base install. Pydantic remains optional. The canvas is served as local files and does not send workflow data to a remote service.
|
|
29
|
+
|
|
30
|
+
## Extending nodes
|
|
31
|
+
|
|
32
|
+
### Python tools
|
|
33
|
+
|
|
34
|
+
Write a trusted Python module with `register(add_tool)`, then launch using `python3 run_studio.py --plugin path/to/tools.py`. Register a concise name, callable and description. A tool callable receives `(value, config)` and returns a JSON-compatible value. Put credentials and configured library clients in the plugin process environment or its local configuration, never in workflow JSON. Plugin modules are trusted code and run with full user permissions.
|
|
35
|
+
|
|
36
|
+
### Model adapters
|
|
37
|
+
|
|
38
|
+
Chat Model and Agent nodes use OpenRouter through the official Python SDK. The `.env` file is loaded at Studio startup and API keys stay out of workflow JSON and execution traces. The graph itself is scheduled by the C++ runtime. Add other providers only after defining their configuration, response/error mapping, limits, cancellation behavior, and tests.
|
|
39
|
+
|
|
40
|
+
### Structured outputs
|
|
41
|
+
|
|
42
|
+
Fields currently support flat names and strict primitive/object/array types. The built-in validator works without dependencies. Optional Pydantic mode uses Pydantic v2 models generated from that list. Nested schemas, custom validators, unions and secret fields are future UI features. Python users who need custom object types can validate them inside a registered Python tool.
|
|
43
|
+
|
|
44
|
+
### Native C++ nodes
|
|
45
|
+
|
|
46
|
+
Use the C++ builder API for native applications. Native equality-condition nodes can also be constructed through the current C API. The graph designer exposes Python integrations as nodes. A general user-authored native-code plugin system would require a stable ABI, safe compilation strategy, and platform builds; it is not part of this release.
|
|
47
|
+
|
|
48
|
+
## Performance and scale
|
|
49
|
+
|
|
50
|
+
C++ removes the scheduler from the Python runtime and makes its transitions native. Python callbacks, model requests, Pydantic and library processing remain Python. For network-bound LLM flows, remote model time will often dominate. Measure complete representative workflows before drawing a speed conclusion.
|
|
51
|
+
|
|
52
|
+
The UI caps a workflow at 100 nodes and four simultaneous runs. The native runtime executes each graph sequentially and stores state in local files. It is not yet a distributed service, a sandbox, or a database-backed workflow platform.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# PyPI release checklist
|
|
2
|
+
|
|
3
|
+
The distribution name is `graphcore-studio`; the Python import is `graphcore`. The shorter PyPI name `graphcore` is already registered by another project. Recheck `graphcore-studio` availability at upload time. Keep the version synchronized in `pyproject.toml`, `CMakeLists.txt`, and `python/graphcore/__init__.py`.
|
|
4
|
+
|
|
5
|
+
## Build and validate locally
|
|
6
|
+
|
|
7
|
+
Use Python 3.9 or newer, a C++17 compiler, and a working network connection for isolated build dependencies:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
python -m pip install --upgrade build twine
|
|
11
|
+
python -m build --sdist --wheel
|
|
12
|
+
python -m twine check dist/graphcore_studio-*.whl dist/graphcore_studio-*.tar.gz
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The source archive builds a native library at install time. Wheels contain the C++ shared library and Studio assets. `.github/workflows/wheels.yml` builds and tests CPython 3.9–3.13 wheels for Linux x86-64/ARM64, Windows x86-64, and macOS x86-64/ARM64. Run it manually from GitHub Actions or push a `v*` tag. The `graphcore-studio-distributions` artifact contains the combined wheels and source archive after Twine validation. Download those exact files for TestPyPI and release.
|
|
16
|
+
|
|
17
|
+
Never upload old `*-source.zip` files from `dist/`. Upload only the `.whl` and `.tar.gz` artifacts emitted by the build command. Inspect the archives before release: `studio/data/` is intentionally excluded because it may contain local workflows, run history, and secrets.
|
|
18
|
+
|
|
19
|
+
## TestPyPI first
|
|
20
|
+
|
|
21
|
+
Download the `graphcore-studio-distributions` artifact from a successful workflow run, create a TestPyPI project and API token, then upload the exact validated artifacts:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
python -m twine upload --repository testpypi dist/*.whl dist/*.tar.gz
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Install and test from a clean environment against TestPyPI before publishing to PyPI. Avoid pasting API tokens into the repository or chat; configure them through Twine's secure prompt or a protected environment variable.
|
|
28
|
+
|
|
29
|
+
## Publish
|
|
30
|
+
|
|
31
|
+
Confirm the package name is still available, the GitHub repository and release tag are correct, and the final artifacts have passed CI. Then publish those same tested artifacts:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
python -m twine upload dist/*.whl dist/*.tar.gz
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
PyPI does not allow replacing files for an existing version. Increment the version in both metadata files for every subsequent release.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Roadmap
|
|
2
|
+
|
|
3
|
+
## Shipped in this local Studio prototype
|
|
4
|
+
|
|
5
|
+
- Drag-and-drop canvas with reusable JSON templates, condition routes and agent handoff flows.
|
|
6
|
+
- Supervisor-to-specialist example, Python tools, output inspection, execution trace and run history.
|
|
7
|
+
- Structured output with a dependency-free validator and optional Pydantic v2 integration.
|
|
8
|
+
- OpenRouter SDK adapter, human approval checkpoints, workflow export/import and local save.
|
|
9
|
+
- C++17 sequential scheduler and file checkpoints behind a C ABI, launched with one command.
|
|
10
|
+
- Loopback-only server, run input limits, per-launch write token and origin validation.
|
|
11
|
+
|
|
12
|
+
## Remaining work before production
|
|
13
|
+
|
|
14
|
+
1. Persisted parallel supersteps, explicit fan-out/join groups, stable reducers, and durable task results.
|
|
15
|
+
2. SQLite/PostgreSQL transaction backends, checkpoint fsync policy, integrity checks, schema migration and retention.
|
|
16
|
+
3. Streaming provider responses, classified retries, idempotent effect journal, and Python cancellation/deadline propagation.
|
|
17
|
+
4. Nested JSON Schema/Pydantic models, user-defined validators, typed output ports, and richer state/secret handling.
|
|
18
|
+
5. Run the multi-platform wheel release workflow and verify Linux, macOS, and Windows artifacts before publishing.
|
|
19
|
+
6. Trace redaction/export, automated frontend accessibility and visual regression checks, and a full developer guide.
|
|
20
|
+
7. Authentication and worker leases only if a remote/team deployment becomes a goal; current Studio remains a local single-user tool.
|
|
21
|
+
|
|
22
|
+
## Release gates
|
|
23
|
+
|
|
24
|
+
Pass the compiler-only build and CTest on Linux, macOS and Windows. Test Pydantic-enabled and dependency-free environments, real restart/interrupt recovery, OpenRouter request limits and JSON responses, malformed workflow import, cross-origin write rejection and cancellation behavior. Benchmark native scheduler time separately from Python/model calls. Do not publish an overall speedup figure without comparable workloads and measured data.
|