Skip to main content

Custom agent features

Create agents that can dynamically enable or disable features based on workspace configuration. Agents can access user preferences through workspace_options and respond accordingly.

Reference implementation in this GitHub repository.

Custom agent features - on and off Custom agent features - on and off

Architecture

Configure custom features in your agent's descriptor and access them through the query request payload. Features can have default states, descriptions, and labels.

agents.json configuration with custom features:

return JSONResponse(content={
"vanilla_agent_custom_features": {
"name": "Vanilla Agent Custom Features",
"description": "A simple agent that reports its feature status.",
"endpoints": {"query": "/v1/query"},
"features": {
"streaming": True,
"widget-dashboard-select": False,
"widget-dashboard-search": False,
"deep-research": {
"label": "Deep Research",
"default": False,
"description": "Allows the copilot to do deep research",
},
"web-search": {
"label": "Web Search",
"default": True,
"description": "Allows the copilot to search the web.",
},
"model": {
"label": "Model",
"type": "select",
"default": "claude-sonnet-4-20250514",
"description": "Select the LLM model to use.",
"options": [
{"label": "Claude Opus 4", "value": "claude-opus-4-0-20250514"},
{"label": "Claude Sonnet 4", "value": "claude-sonnet-4-20250514"},
{"label": "GPT-4o", "value": "gpt-4o"},
{"label": "GPT-4o mini", "value": "gpt-4o-mini"},
],
},
"agent-name": {
"label": "Name of Agent",
"type": "text",
"default": "Example Agent",
"description": "Set the name the agent uses to introduce itself.",
"placeholder": "e.g. My Custom Agent",
},
},
}
})

Feature configuration

  • Boolean features: Simple on/off toggles.
    • Objects with label, default, and description properties
  • Text features: Free-form string input.
    • Objects with label, type: "text", default, description, and optional placeholder
  • Select features: Dropdown selection.
    • Objects with label, type: "select", default, description, and options (array of {label, value} objects)

Query flow

  • User enables/disables features in workspace settings
  • Features are passed to agent via workspace_options in request payload
  • Agent checks enabled features and adjusts behavior accordingly
  • Agent can report feature status back to user
  • Response content varies based on active features

OpenBB AI SDK

  • QueryRequest.workspace_options: Dictionary of option values keyed by option id
  • message_chunk(text): Streams response content with feature-aware messaging
  • Boolean features appear as true or false: workspace_options["web-search"]
  • Text and select features appear as string values: workspace_options["model"]

Core logic

from typing import AsyncGenerator
from openbb_ai import message_chunk
from openbb_ai.models import MessageChunkSSE, QueryRequest
from sse_starlette.sse import EventSourceResponse

@app.post("/v1/query")
async def query(request: QueryRequest) -> EventSourceResponse:
# Access workspace options from request payload
# Example: {"web-search": true, "model": "gpt-4o", "agent-name": "My Bot"}
workspace_options = getattr(request, "workspace_options", {}) or {}

# Check boolean features
deep_research_enabled = bool(workspace_options.get("deep-research", False))
web_search_enabled = bool(workspace_options.get("web-search", True))

# Read text/select feature values
model = workspace_options.get("model", "claude-sonnet-4-20250514")
agent_name = workspace_options.get("agent-name", "Example Agent")

# Build feature status message
features_msg = (
f"- Deep Research: {'✅ Enabled' if deep_research_enabled else '❌ Disabled'}\n"
f"- Web Search: {'✅ Enabled' if web_search_enabled else '❌ Disabled'}\n"
f"- Model: {model}\n"
f"- Agent Name: {agent_name}"
)

# Include feature status in system prompt
openai_messages = [
{
"role": "system",
"content": (
f'Your name is "{agent_name}".\n'
"Greet the user and let them know their current feature settings:\n"
f"{features_msg}\n"
"Keep your response brief and friendly."
),
}
]

# Add conversation history
for message in request.messages:
if message.role == "human":
openai_messages.append({"role": "user", "content": message.content})
elif message.role == "ai" and isinstance(message.content, str):
openai_messages.append({"role": "assistant", "content": message.content})

async def execution_loop() -> AsyncGenerator[MessageChunkSSE, None]:
client = openai.AsyncOpenAI()
async for event in await client.chat.completions.create(
model="gpt-4o",
messages=openai_messages,
stream=True,
):
if chunk := event.choices[0].delta.content:
yield message_chunk(chunk)

return EventSourceResponse(
content=(
event.model_dump(exclude_none=True) async for event in execution_loop()
),
media_type="text/event-stream",
)

Feature types

Boolean features

Simple on/off toggles with a label and default state:

"features": {
"deep-research": {
"label": "Deep Research",
"default": False,
"description": "Allows the copilot to do deep research",
}
}

Text features

Free-form string input with an optional placeholder:

"features": {
"agent-name": {
"label": "Name of Agent",
"type": "text",
"default": "Example Agent",
"description": "Set the name the agent uses to introduce itself.",
"placeholder": "e.g. My Custom Agent",
}
}

Select features

Dropdown selection with a list of options:

"features": {
"model": {
"label": "Model",
"type": "select",
"default": "claude-sonnet-4-20250514",
"description": "Select the LLM model to use.",
"options": [
{"label": "Claude Opus 4", "value": "claude-opus-4-0-20250514"},
{"label": "Claude Sonnet 4", "value": "claude-sonnet-4-20250514"},
{"label": "GPT-4o", "value": "gpt-4o"},
{"label": "GPT-4o mini", "value": "gpt-4o-mini"},
],
}
}

Reading feature values

Boolean, text, and select values are sent in workspace_options as a dictionary keyed by option id:

workspace_options = getattr(request, "workspace_options", {}) or {}

# Boolean features
if workspace_options.get("deep-research", False):
# Enable deep research capabilities
pass

# Text/select features
model = workspace_options.get("model", "claude-sonnet-4-20250514")
agent_name = workspace_options.get("agent-name", "Example Agent")