Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/README-nodes.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ For information on testing nodes, see [README-node-testing.md](README-node-testi
| `llm_xai` | xAI (Grok) | |
| `llm_vertex` | Google Vertex AI | |
| `llm_ibm_watson` | IBM Watson | |
| `llm_nebius` | Nebius Token Factory (OpenAI-compatible) | [README](../nodes/src/nodes/llm_nebius/README.md) |
| `llm_vision_mistral` | Mistral Vision (multimodal, image-to-text) | [README](../nodes/src/nodes/llm_vision_mistral/README.md) |

## Vector Databases
Expand Down Expand Up @@ -99,6 +100,16 @@ The `core` module provides built-in connectors for OneDrive, SharePoint, Google
| `text_output` | Text output |
| `local_text_output` | Local text file output |

## Agent Tools

Tool nodes (`classType: ["tool"]`) expose capabilities to agents via the control-plane invoke channel rather than data lanes.

| Node | Description | Documentation |
| -------------------- | ------------------------------------ | ------------------------------------------------------------ |
| `tool_tavily` | Tavily real-time web search for agents | [README](../nodes/src/nodes/tool_tavily/README.md) |

The `tool_tavily` node pairs with `llm_nebius` and `agent_deepagent` to build Nebius Agentic Search — see `examples/nebius-agentic-search.pipe`.

## Internal

| Node | Description |
Expand Down
153 changes: 153 additions & 0 deletions examples/nebius-agentic-search.pipe
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
{
"name": "Nebius Agentic Search",
"description": "A web-research agent powered by Nebius (Llama 3.3 70B) and Tavily real-time search. Ask any question and the agent will search the web, refine its queries, and return a cited answer.",
"components": [
{
"id": "chat_1",
"provider": "chat",
"name": "Trigger",
"config": {
"hideForm": true,
"mode": "Source",
"parameters": {},
"type": "chat"
},
"ui": {
"position": {
"x": 20,
"y": 200
},
"measured": {
"width": 150,
"height": 66
},
"nodeType": "default",
"formDataValid": true
}
},
{
"id": "agent_deepagent_1",
"provider": "agent_deepagent",
"name": "Nebius Agentic Search",
"config": {
"instructions": [
"You are an agentic web-research assistant.",
"Use the tavily tool to find current information; refine your query and search again when results are weak.",
"Cite the source URLs you used and answer concisely."
],
"parameters": {}
},
"ui": {
"position": {
"x": 240,
"y": 200
},
"measured": {
"width": 150,
"height": 86
},
"nodeType": "default",
"formDataValid": true
},
"input": [
{
"lane": "questions",
"from": "chat_1"
}
]
},
{
"id": "llm_nebius_1",
"provider": "llm_nebius",
"name": "Nebius LLM",
"config": {
"profile": "llama-3-3-70b",
"llama-3-3-70b": {
"apikey": "${NEBIUS_API_KEY}"
},
"parameters": {}
},
"ui": {
"position": {
"x": 130,
"y": 380
},
"measured": {
"width": 150,
"height": 66
},
"nodeType": "default",
"formDataValid": true
},
"control": [
{
"classType": "llm",
"from": "agent_deepagent_1"
}
]
},
{
"id": "tool_tavily_1",
"provider": "tool_tavily",
"name": "Tavily",
"config": {
"apikey": "${TAVILY_API_KEY}",
"maxResults": 5,
"searchDepth": "advanced",
"topic": "general"
},
"ui": {
"position": {
"x": 350,
"y": 380
},
"measured": {
"width": 150,
"height": 37
},
"nodeType": "default",
"formDataValid": true
},
"control": [
{
"classType": "tool",
"from": "agent_deepagent_1"
}
]
},
{
"id": "response_answers_1",
"provider": "response_answers",
"config": {
"laneName": "answers"
},
"ui": {
"position": {
"x": 447,
"y": 222
},
"measured": {
"width": 150,
"height": 66
},
"nodeType": "default",
"formDataValid": true
},
"input": [
{
"lane": "answers",
"from": "agent_deepagent_1"
}
]
}
],
"source": "chat_1",
"project_id": "c5ba857f-30ff-4f3d-b5bf-9ee90d3ee33b",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider omitting the hardcoded project_id in the example pipeline.

The project_id field is optional and ties the example to a specific project UUID. For an example that users may copy, omitting this field would make the template more immediately usable without requiring users to replace the ID. Alternatively, a comment could indicate that users should set their own project ID.

📝 Optional simplification
   ],
   "source": "chat_1",
-  "project_id": "c5ba857f-30ff-4f3d-b5bf-9ee90d3ee33b",
   "viewport": {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"project_id": "c5ba857f-30ff-4f3d-b5bf-9ee90d3ee33b",
],
"source": "chat_1",
"viewport": {
"x": 0,
"y": 0,
"zoom": 1
},
"version": 1,
"docRevision": 1
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/nebius-agentic-search.pipe` at line 145, The example pipeline
contains a hardcoded "project_id" value that ties the template to a specific
UUID; remove the "project_id": "c5ba857f-30ff-4f3d-b5bf-9ee90d3ee33b" entry from
the examples/nebius-agentic-search.pipe example (or replace it with a short
comment like // set your project_id here) so the example is generic and
copy-paste ready; locate the "project_id" key in the JSON block and either
delete that line or change it to a placeholder/comment accordingly.

"viewport": {
"x": 15.5,
"y": -120.9,
"zoom": 1.04
},
"version": 1,
"docRevision": 1
}
101 changes: 101 additions & 0 deletions nodes/src/nodes/llm_nebius/IGlobal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# =============================================================================
# MIT License
# Copyright (c) 2026 Aparavi Software AG
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# =============================================================================

import os
from typing import Optional
from rocketlib import IGlobalBase, warning
from ai.common.config import Config
from ai.common.chat import ChatBase


class IGlobal(IGlobalBase):
"""Global handler for the Nebius Token Factory LLM node."""

_chat: Optional[ChatBase] = None

_VALIDATION_PROMPT = 'Hi'
_BASE_URL = 'https://api.tokenfactory.nebius.com/v1/'

def _resolve_apikey(self, config) -> str:
return str(config.get('apikey') or os.environ.get('NEBIUS_API_KEY', '')).strip()

def validateConfig(self):
"""Probe the model with a 1-token request to validate key + model at save time."""
from depends import depends # type: ignore

requirements = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'requirements.txt')
depends(requirements)

try:
from openai import (
OpenAI,
APIStatusError,
OpenAIError,
AuthenticationError,
RateLimitError,
APIConnectionError,
)

config = Config.getNodeConfig(self.glb.logicalType, self.glb.connConfig)
apikey = self._resolve_apikey(config)
model = config.get('model')
if not model or not apikey:
return
try:
client = OpenAI(api_key=apikey, base_url=self._BASE_URL)
client.chat.completions.create(
model=model,
messages=[{'role': 'user', 'content': self._VALIDATION_PROMPT}],
max_tokens=1,
)
except RateLimitError:
return
except APIStatusError as e:
status = getattr(e, 'status_code', None) or getattr(e, 'status', None)
if status == 429:
return
warning(f'Nebius validation error {status}: {e}')
return
except (AuthenticationError, APIConnectionError, OpenAIError) as e:
warning(str(e))
return
except Exception as e:
warning(str(e))

def beginGlobal(self):
"""Initialize the Nebius chat client."""
from depends import depends # type: ignore

requirements = os.path.dirname(os.path.realpath(__file__)) + '/requirements.txt'
depends(requirements)

from .nebius import Chat

bag = self.IEndpoint.endpoint.bag
config = Config.getNodeConfig(self.glb.logicalType, self.glb.connConfig)
if not self._resolve_apikey(config):
raise ValueError('Nebius API key is required.')
self._chat = Chat(self.glb.logicalType, config, bag)

def endGlobal(self):
self._chat = None
28 changes: 28 additions & 0 deletions nodes/src/nodes/llm_nebius/IInstance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# =============================================================================
# MIT License
# Copyright (c) 2026 Aparavi Software AG
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# =============================================================================

from ai.common.llm_base import LLMBase


class IInstance(LLMBase):
pass
44 changes: 44 additions & 0 deletions nodes/src/nodes/llm_nebius/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: Nebius
date: 2026-06-01
sidebar_position: 1
---

<head>
<title>Nebius - RocketRide Documentation</title>
</head>

## What it does

Connects Nebius Token Factory-hosted models to your pipeline via an OpenAI-compatible API. The base URL is fixed at `https://api.tokenfactory.nebius.com/v1/` — no endpoint configuration needed. Used primarily as an `llm` invoke connection by agents (including Nebius Agentic Search) and other nodes that need an LLM. Can also be used directly via lanes.

**Lanes:**

| Lane in | Lane out | Description |
| ----------- | --------- | ---------------------------------------------------- |
| `questions` | `answers` | Send a question directly, receive a generated answer |

## Configuration

| Field | Description |
| ------- | ----------------------------------------------- |
| Model | Model profile or custom model ID (see below) |
| API Key | Nebius Token Factory API key (`NEBIUS_API_KEY`) |

The API key can be supplied via the node's **API Key** field or the `NEBIUS_API_KEY` environment variable.

## Model profiles

| Profile | Model ID | Context |
| -------------------------- | --------------------------------------- | ------- |
| Llama 3.3 70B _(default)_ | `meta-llama/Llama-3.3-70B-Instruct` | 131,072 |
| Qwen3 235B | `Qwen/Qwen3-235B-A22B` | 131,072 |
| DeepSeek V3 | `deepseek-ai/DeepSeek-V3` | 131,072 |
| Custom | any Token Factory model ID | 131,072 |

**Custom** — specify any Nebius Token Factory model ID and token limit directly.

## Upstream docs

- [Nebius Token Factory model catalogue](https://tokenfactory.nebius.com/models)
- [Nebius AI documentation](https://docs.nebius.com)
39 changes: 39 additions & 0 deletions nodes/src/nodes/llm_nebius/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# =============================================================================
# MIT License
# Copyright (c) 2026 Aparavi Software AG
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# =============================================================================

from .IGlobal import IGlobal
from .IInstance import IInstance


def getChat():
"""Get the Chat class from the module."""
from .nebius import Chat

return Chat


__all__ = [
'IGlobal',
'IInstance',
'getChat',
]
Loading
Loading