From 18991aa212aeacf27d3c0cc8b1c1f6fe177bb114 Mon Sep 17 00:00:00 2001 From: Amogh Sunil Date: Thu, 11 Jun 2026 16:40:42 +0530 Subject: [PATCH 1/8] Add import page with before/after cards and cookbook pages for all 12 frameworks - New ImportSection with two paths: file system agents (opengap import) and code-based frameworks (cookbook) - Expandable cards showing before/after directory structure for each agent - Cookbook pages for 5 file system agents: Claude Code, Cursor, Gemini CLI, Codex, OpenCode - Cookbook pages for 6 code-based frameworks: LangGraph, CrewAI, AutoGen, OpenAI Agents SDK, Claude SDK, Google ADK - Import page added to sidebar under Features (above Export) - All cookbook pages routed via OpenGAPDocsPage --- src/components/ImportSection.tsx | 384 ++++++++++++++++++ src/components/opengap/OpenGAPSidebar.tsx | 1 + .../opengap/cookbook/CookbookAutoGen.tsx | 247 +++++++++++ .../opengap/cookbook/CookbookClaudeCode.tsx | 133 ++++++ .../opengap/cookbook/CookbookClaudeSDK.tsx | 273 +++++++++++++ .../opengap/cookbook/CookbookCodex.tsx | 145 +++++++ .../opengap/cookbook/CookbookCrewAI.tsx | 235 +++++++++++ .../opengap/cookbook/CookbookCursor.tsx | 115 ++++++ .../opengap/cookbook/CookbookGeminiCLI.tsx | 114 ++++++ .../opengap/cookbook/CookbookGoogleADK.tsx | 340 ++++++++++++++++ .../opengap/cookbook/CookbookLangChain.tsx | 232 +++++++++++ .../opengap/cookbook/CookbookLangGraph.tsx | 202 +++++++++ .../opengap/cookbook/CookbookOpenAIAgents.tsx | 311 ++++++++++++++ .../opengap/cookbook/CookbookOpenCode.tsx | 119 ++++++ src/pages/OpenGAPDocsPage.tsx | 24 ++ 15 files changed, 2875 insertions(+) create mode 100644 src/components/ImportSection.tsx create mode 100644 src/components/opengap/cookbook/CookbookAutoGen.tsx create mode 100644 src/components/opengap/cookbook/CookbookClaudeCode.tsx create mode 100644 src/components/opengap/cookbook/CookbookClaudeSDK.tsx create mode 100644 src/components/opengap/cookbook/CookbookCodex.tsx create mode 100644 src/components/opengap/cookbook/CookbookCrewAI.tsx create mode 100644 src/components/opengap/cookbook/CookbookCursor.tsx create mode 100644 src/components/opengap/cookbook/CookbookGeminiCLI.tsx create mode 100644 src/components/opengap/cookbook/CookbookGoogleADK.tsx create mode 100644 src/components/opengap/cookbook/CookbookLangChain.tsx create mode 100644 src/components/opengap/cookbook/CookbookLangGraph.tsx create mode 100644 src/components/opengap/cookbook/CookbookOpenAIAgents.tsx create mode 100644 src/components/opengap/cookbook/CookbookOpenCode.tsx diff --git a/src/components/ImportSection.tsx b/src/components/ImportSection.tsx new file mode 100644 index 0000000..4fa454d --- /dev/null +++ b/src/components/ImportSection.tsx @@ -0,0 +1,384 @@ +import { motion, AnimatePresence } from "framer-motion"; +import { Download, FolderOpen, Code2, ChevronDown, Copy, Check } from "lucide-react"; +import { useState } from "react"; + +interface AgentEntry { + label: string; + desc: string; + cmd: string; + cookbookId?: string; + before: string; + after: string; +} + +const fsAgents: AgentEntry[] = [ + { + label: "Claude Code", + desc: "Reads CLAUDE.md, memory/, and .claude/ config — imports identity, skills, and rules.", + cmd: "$ opengap import -f claude-code", + cookbookId: "cookbook-claude-code", + before: `my-project/ +├── CLAUDE.md +├── memory/ +│ └── preferences.md +└── .claude/ + └── settings.json`, + after: `my-agent/ +├── agent.yaml +├── SOUL.md +├── RULES.md +└── skills/ + └── tdd/SKILL.md`, + }, + { + label: "Cursor", + desc: "Reads .cursorrules and workspace settings — imports agent rules and behavior.", + cmd: "$ opengap import -f cursor", + cookbookId: "cookbook-cursor", + before: `my-project/ +├── .cursorrules +└── .cursor/ + └── settings.json`, + after: `my-agent/ +├── agent.yaml +├── SOUL.md +└── RULES.md`, + }, + { + label: "Gemini CLI", + desc: "Reads GEMINI.md and system instructions — imports identity and tool config.", + cmd: "$ opengap import -f gemini", + cookbookId: "cookbook-gemini-cli", + before: `my-project/ +└── GEMINI.md`, + after: `my-agent/ +├── agent.yaml +├── SOUL.md +└── RULES.md`, + }, + { + label: "Codex", + desc: "Reads AGENTS.md and codex config — imports agent identity and tool stubs.", + cmd: "$ opengap import -f codex", + cookbookId: "cookbook-codex", + before: `my-project/ +└── AGENTS.md`, + after: `my-agent/ +├── agent.yaml +├── SOUL.md +├── RULES.md +└── tools/ + ├── run-tests.yaml + └── lint-code.yaml`, + }, + { + label: "OpenCode", + desc: "Reads opencode config and system prompt — imports identity and tool definitions.", + cmd: "$ opengap import -f opencode", + cookbookId: "cookbook-opencode", + before: `my-project/ +└── .opencode/ + └── config.json`, + after: `my-agent/ +├── agent.yaml +├── SOUL.md +└── RULES.md`, + }, +]; + +const codeFrameworks: AgentEntry[] = [ + { + label: "LangGraph", + desc: "Translates StateGraph nodes, edges, and tools into OpenGAP agents and tool YAMLs.", + cmd: "$ opengap import -f langgraph", + cookbookId: "cookbook-langgraph", + before: `langgraph-project/ +├── graph.py +├── nodes/ +│ ├── researcher.py +│ └── writer.py +└── tools/ + ├── search_web.py + └── write_report.py`, + after: `react-agent/ +├── agent.yaml +├── SOUL.md +├── RULES.md +├── tools/ +│ ├── search-web.yaml +│ └── write-report.yaml +└── agents/ + ├── researcher/agent.yaml + └── writer/agent.yaml`, + }, + { + label: "CrewAI", + desc: "Maps Crew, Agent roles/goals, and Task definitions to agents/ and workflows/.", + cmd: "$ opengap import -f crewai", + cookbookId: "cookbook-crewai", + before: `crewai-project/ +├── crew.py +├── agents/ +│ ├── analyst.py +│ └── writer.py +├── tasks/ +│ └── research_tasks.py +└── tools/ + └── yahoo_finance.py`, + after: `example-crewai/ +├── agent.yaml +├── SOUL.md +├── agents/ +│ ├── financial-analyst/agent.yaml +│ └── communications-specialist/agent.yaml +├── tools/ +│ ├── yahoo-finance-news.yaml +│ └── google-jobs.yaml +└── workflows/ + └── financial-analysis.yaml`, + }, + { + label: "AutoGen", + desc: "Converts ConversableAgent teams, tools, and group chats into OpenGAP agents.", + cmd: "$ opengap import -f autogen", + cookbookId: "cookbook-autogen", + before: `autogen-project/ +├── team.py +├── agents/ +│ ├── assistant.py +│ └── user_proxy.py +└── tools/ + └── code_execution.py`, + after: `autogen/ +├── agent.yaml +├── SOUL.md +├── agents/ +│ ├── assistant/agent.yaml +│ ├── user-proxy/agent.yaml +│ └── code-executor/agent.yaml +├── tools/ +│ ├── code-execution.yaml +│ └── function-tool.yaml +└── workflows/ + └── agentchat-pipeline.yaml`, + }, + { + label: "OpenAI Agents SDK", + desc: "Maps Agent, handoffs, and @function_tool to agent.yaml, agents/, and tools/.", + cmd: "$ opengap import -f openai-agents", + cookbookId: "cookbook-openai-agents", + before: `openai-agents-project/ +├── main.py +└── tools/ + ├── faq_lookup.py + └── update_seat.py`, + after: `openai-agents/ +├── agent.yaml +├── SOUL.md +├── agents/ +│ ├── faq-agent/agent.yaml +│ └── seat-booking-agent/agent.yaml +└── tools/ + ├── faq-lookup.yaml + └── update-seat.yaml`, + }, + { + label: "Claude SDK", + desc: "Converts Anthropic SDK agents — tools[], SYSTEM_PROMPT, and multi-turn loops into OpenGAP format.", + cmd: "$ opengap import -f claude-sdk", + cookbookId: "cookbook-claude-sdk", + before: `anthropic-project/ +├── agent.py +└── tools/ + ├── get_customer_info.py + ├── get_order_details.py + └── cancel_order.py`, + after: `anthropic-agent/ +├── agent.yaml +├── SOUL.md +└── tools/ + ├── get-customer-info.yaml + ├── get-order-details.yaml + └── cancel-order.yaml`, + }, + { + label: "Google ADK", + desc: "Converts Agent, sub_agents, and AgentTool hierarchies into nested OpenGAP dirs.", + cmd: "$ opengap import -f google-adk", + cookbookId: "cookbook-google-adk", + before: `adk-project/ +├── agent.py +└── sub_agents/ + ├── inspiration/ + │ ├── agent.py + │ └── prompt.py + ├── planning/agent.py + └── booking/agent.py`, + after: `adk-samples/ +├── agent.yaml +├── SOUL.md +└── agents/ + ├── inspiration-agent/ + │ ├── agent.yaml + │ └── agents/poi-agent/agent.yaml + ├── planning-agent/agent.yaml + └── booking-agent/agent.yaml`, + }, +]; + +function CopyButton({ text }: { text: string }) { + const [copied, setCopied] = useState(false); + return ( + + ); +} + +function AgentCard({ entry, index }: { entry: AgentEntry; index: number }) { + const [open, setOpen] = useState(false); + + return ( + + + + + {open && ( + +
+
+

Before

+
{entry.before}
+
+
+

After (OpenGAP)

+
{entry.after}
+ {entry.cookbookId && ( + + View full conversion cookbook → + + )} +
+
+
+ {entry.cmd} +
+
+ )} +
+
+ ); +} + +export function ImportSection() { + return ( +
+
+ + +
+ +

Import Any Agent into OpenGAP

+
+

+ Two paths to bring your existing agents into OpenGAP format. +

+ +
+
+
+ + 1 — File System Agents +
+

+ Agents that live as config files (Claude Code, Cursor, Gemini CLI, Codex, OpenCode). +
opengap import reads their files directly and outputs OpenGAP format. +

+
+
+
+ + 2 — Code-Based Frameworks +
+

+ Agents defined in code (LangGraph, CrewAI, AutoGen, etc.). +
Follow the cookbook to manually map your framework's concepts to OpenGAP files. +

+
+
+
+ + {/* Path 1 */} + +
+ +

1 — File System Agents

+
+
+ {fsAgents.map((e, i) => )} +
+
+ + {/* Path 2 */} + +
+ +

2 — Code-Based Frameworks

+
+
+ {codeFrameworks.map((e, i) => )} +
+
+ +
+
+ ); +} diff --git a/src/components/opengap/OpenGAPSidebar.tsx b/src/components/opengap/OpenGAPSidebar.tsx index a9408fa..1ec6844 100644 --- a/src/components/opengap/OpenGAPSidebar.tsx +++ b/src/components/opengap/OpenGAPSidebar.tsx @@ -20,6 +20,7 @@ export const opengapSidebarGroups = [ slug: "features", items: [ { id: "cli", label: "CLI" }, + { id: "import", label: "Import" }, { id: "export", label: "Export" }, { id: "adapters", label: "Adapters" }, ], diff --git a/src/components/opengap/cookbook/CookbookAutoGen.tsx b/src/components/opengap/cookbook/CookbookAutoGen.tsx new file mode 100644 index 0000000..f6884db --- /dev/null +++ b/src/components/opengap/cookbook/CookbookAutoGen.tsx @@ -0,0 +1,247 @@ +import { motion } from "framer-motion"; +import { CodeBlock } from "@/components/gitAgent/CodeBlock"; + +const projectStructure = `autogen-agentchat two-agent reflection pattern +├── primary_agent ← AssistantAgent: drafts and refines responses +└── critic_agent ← AssistantAgent: provides feedback until satisfied`; + +const agentPy = `# Based on microsoft/autogen AgentChat teams tutorial +import asyncio +from autogen_agentchat.agents import AssistantAgent +from autogen_agentchat.conditions import TextMentionTermination +from autogen_agentchat.teams import RoundRobinGroupChat +from autogen_agentchat.ui import Console +from autogen_ext.models.openai import OpenAIChatCompletionClient + +# Define a tool for the primary agent +async def web_search(query: str) -> str: + """Find information on the web about a given topic.""" + # real impl calls a search API + return f"Search results for: {query}" + +model_client = OpenAIChatCompletionClient( + model="gpt-4o-2024-08-06", +) + +# Primary agent drafts responses using tools +primary_agent = AssistantAgent( + name="primary", + model_client=model_client, + tools=[web_search], + system_message="""You are a helpful research assistant. +Search the web to find accurate, up-to-date information before answering. +Cite your sources. Revise your response based on the critic's feedback.""", +) + +# Critic agent reviews and provides feedback +critic_agent = AssistantAgent( + name="critic", + model_client=model_client, + system_message="""You are a constructive critic. Review the primary agent's response for: +- Factual accuracy and source quality +- Completeness and clarity +- Any missing context or caveats + +Respond with 'APPROVE' once the response meets the quality bar.""", +) + +# Stop when critic approves +termination = TextMentionTermination("APPROVE") +team = RoundRobinGroupChat( + [primary_agent, critic_agent], + termination_condition=termination, +) + +async def main(): + await Console(team.run_stream( + task="What are the key differences between LangGraph and AutoGen?" + )) + +asyncio.run(main())`; + +const agentYaml = `spec_version: 0.1.0 +name: primary +version: 0.1.0 +description: Research assistant that searches the web and refines responses based on feedback +model: + preferred: gpt-4o-2024-08-06 +tools: + - web-search +agents: + critic: + description: Reviews research responses for accuracy, completeness, and clarity + delegation: + mode: auto`; + +const soulMd = `# Soul + +## Core Identity +You are a helpful research assistant. + +## Purpose +Find accurate, up-to-date information by searching the web before answering. +Revise your response based on the critic's feedback.`; + +const rulesMd = `# Rules + +- Always search before answering factual questions +- Always cite sources +- Never fabricate URLs or citations`; + +const subCriticYaml = `spec_version: 0.1.0 +name: critic +version: 0.1.0 +description: Reviews research responses for accuracy, completeness, and clarity +model: + preferred: gpt-4o-2024-08-06`; + +const subCriticSoul = `# Soul + +## Core Identity +You are a constructive critic reviewing research responses. + +## Review Criteria +- Factual accuracy and source quality +- Completeness and clarity +- Missing context or caveats + +## Behavior +Respond with 'APPROVE' once the response meets the quality bar.`; + +const toolWebSearch = `name: web-search +description: Find information on the web about a given topic. +input_schema: + type: object + properties: + query: + type: string + description: The search topic or question + required: + - query +implementation: + type: script + path: tools/web_search.py + runtime: python3 + timeout: 30`; + +const validateCmd = `opengap validate -d ./primary-agent-opengap +opengap info -d ./primary-agent-opengap`; + +const mapping = [ + ["AssistantAgent.system_message", "SOUL.md (split into identity vs rules by content)"], + ["AssistantAgent.name (kebab-case)", "agent.yaml → name"], + ["model_client model string", "agent.yaml → model.preferred"], + ["AssistantAgent.tools=[web_search] (function names)", "agent.yaml → tools[] + tools/.yaml"], + ["Each tool function docstring + typed args", "tools/.yaml description + input_schema"], + ["Second agent (critic_agent)", "agents/critic/agent.yaml + SOUL.md"], + ["RoundRobinGroupChat / team setup", "stays in framework — runtime orchestration"], + ["TextMentionTermination condition", "stays in framework — loop control"], +]; + +const steps = [ + { step: "1", desc: "Take system_message from the primary AssistantAgent → SOUL.md. Split behavioral rules (search before answering, cite sources) from hard constraints (never fabricate) into RULES.md." }, + { step: "2", desc: "Take the model string from OpenAIChatCompletionClient(model=...) → write to agent.yaml → model.preferred." }, + { step: "3", desc: "For each function in AssistantAgent.tools=[], add a kebab-case name to agent.yaml → tools (web_search → web-search)." }, + { step: "4", desc: "Create tools/.yaml for each tool — use the function docstring as description, typed parameters as input_schema." }, + { step: "5", desc: "For the critic agent, create agents/critic/agent.yaml and SOUL.md using the same mapping. No tools needed since critic has no tools." }, + { step: "6", desc: "RoundRobinGroupChat, TextMentionTermination, and team.run_stream() stay in the Python file — they are runtime orchestration with no OpenGAP equivalent." }, +]; + +export function CookbookAutoGen() { + return ( +
+
+ + +

OpenGAP / Cookbook /

+

AutoGen → OpenGAP

+

+ Based on microsoft/autogen AgentChat — a two-agent reflection pattern + with a primary research agent and a critic agent in a RoundRobinGroupChat. + Each AssistantAgent maps to its own OpenGAP directory; + the team setup and termination condition stay in the framework. +

+
+ + {/* Part 1 */} + +

Part 1 — The AutoGen project

+

Two-agent reflection: primary agent with web search + critic agent for quality control:

+
+ + +
+
+ + {/* Part 2 */} + +

Part 2 — What maps to OpenGAP

+
+
+ AutoGenOpenGAP +
+ {mapping.map(([from, to], i) => ( +
+ {from} + {to} +
+ ))} +
+
+ + {/* Part 3 */} + +

Part 3 — Create the OpenGAP files

+
+
+

Primary agent: agent.yaml:

+ +
+
+

Primary agent: SOUL.md — identity from system_message:

+ +
+
+

Primary agent: RULES.md — hard constraints from system_message:

+ +
+
+

tools/web-search.yaml:

+ +
+
+

Critic: agents/critic/agent.yaml:

+ +
+
+

Critic: agents/critic/SOUL.md:

+ +
+
+
+ + {/* Part 4 */} + +

Part 4 — Validate

+ +
+ + {/* Steps */} + +

What happens step by step

+
+ {steps.map((s, i) => ( + +
+ {s.step} +

{s.desc}

+
+
+ ))} +
+
+ +
+
+ ); +} diff --git a/src/components/opengap/cookbook/CookbookClaudeCode.tsx b/src/components/opengap/cookbook/CookbookClaudeCode.tsx new file mode 100644 index 0000000..268bd18 --- /dev/null +++ b/src/components/opengap/cookbook/CookbookClaudeCode.tsx @@ -0,0 +1,133 @@ +import { motion } from "framer-motion"; +import { CodeBlock } from "@/components/gitAgent/CodeBlock"; + +const projectStructure = `my-project/ +├── CLAUDE.md ← identity, purpose, and rules +├── memory/ +│ └── preferences.md ← persistent memory +└── .claude/ + └── settings.json ← model and tool config`; + +const claudeMd = `# CLAUDE.md (example) +You are a senior TypeScript engineer. Help the user write clean, +well-tested code using Vitest and pnpm. + +Always write tests before implementation (TDD). +Never push directly to main — always use a feature branch. +Prefer composition over inheritance.`; + +const agentYaml = `spec_version: 0.1.0 +name: my-engineer-agent +version: 0.1.0 +description: Senior TypeScript engineer assistant +model: + preferred: claude-sonnet-4-6`; + +const soulMd = `# Soul + +## Core Identity +You are a senior TypeScript engineer. + +## Purpose +Help the user write clean, well-tested code using Vitest and pnpm.`; + +const rulesMd = `# Rules + +- Always write tests before implementation (TDD) +- Never push directly to main — always use a feature branch +- Prefer composition over inheritance`; + +const skillMd = `# TDD Skill + +## What this skill does +Enforces test-driven development — write the test first, +then the implementation. + +## Steps +1. Write a failing test for the desired behavior +2. Write the minimum code to make it pass +3. Refactor without breaking the test`; + +const mapping = [ + ["CLAUDE.md — identity paragraph", "SOUL.md → Core Identity + Purpose"], + ["CLAUDE.md — behavioral rules", "RULES.md"], + ["CLAUDE.md — recurring workflows", "skills//SKILL.md"], + ["memory/preferences.md", "agent context (not imported — runtime state)"], + [".claude/settings.json → model", "agent.yaml → model.preferred"], +]; + +const steps = [ + { step: "1", desc: "Open CLAUDE.md. Copy the identity paragraph (who you are, what you do) into SOUL.md under Core Identity and Purpose." }, + { step: "2", desc: "Extract behavioral rules (\"always\", \"never\", \"prefer\") from CLAUDE.md into RULES.md as a bullet list." }, + { step: "3", desc: "If CLAUDE.md describes a recurring workflow (TDD, PR review, etc.), create a skills//SKILL.md for it." }, + { step: "4", desc: "Read .claude/settings.json for the model name and write it to agent.yaml → model.preferred." }, + { step: "5", desc: "memory/ files are runtime state — they don't map to OpenGAP files. Leave them in place." }, +]; + +export function CookbookClaudeCode() { + return ( +
+
+ + +

OpenGAP / Cookbook /

+

Claude Code → OpenGAP

+

+ Claude Code stores agent identity in CLAUDE.md and runtime memory in memory/. + Converting to OpenGAP means splitting CLAUDE.md into SOUL.md (identity), + RULES.md (behavior), and optionally skills/ for recurring workflows. +

+
+ + +

Part 1 — The Claude Code project

+

Typical Claude Code workspace structure:

+
+ + +
+
+ + +

Part 2 — What maps to OpenGAP

+
+
+ Claude CodeOpenGAP +
+ {mapping.map(([from, to], i) => ( +
+ {from} + {to} +
+ ))} +
+
+ + +

Part 3 — Create the OpenGAP files

+
+

agent.yaml:

+

SOUL.md:

+

RULES.md:

+

skills/tdd/SKILL.md:

+
+
+ + +

What happens step by step

+
+ {steps.map((s, i) => ( + +
+ {s.step} +

{s.desc}

+
+
+ ))} +
+
+ +
+
+ ); +} diff --git a/src/components/opengap/cookbook/CookbookClaudeSDK.tsx b/src/components/opengap/cookbook/CookbookClaudeSDK.tsx new file mode 100644 index 0000000..18f3969 --- /dev/null +++ b/src/components/opengap/cookbook/CookbookClaudeSDK.tsx @@ -0,0 +1,273 @@ +import { motion } from "framer-motion"; +import { CodeBlock } from "@/components/gitAgent/CodeBlock"; + +const projectStructure = `anthropics/anthropic-cookbook/ +└── tool_use/ + └── customer_service_agent.ipynb ← tools, system prompt, agent loop`; + +const agentPy = `# customer_service_agent.ipynb — key code cells +import anthropic + +client = anthropic.Client() +MODEL_NAME = "claude-opus-4-1" + +tools = [ + { + "name": "get_customer_info", + "description": "Retrieves customer information based on their customer ID.", + "input_schema": { + "type": "object", + "properties": { + "customer_id": { + "type": "string", + "description": "The unique identifier for the customer.", + } + }, + "required": ["customer_id"], + }, + }, + { + "name": "get_order_details", + "description": "Retrieves the details of a specific order based on the order ID.", + "input_schema": { + "type": "object", + "properties": { + "order_id": { + "type": "string", + "description": "The unique identifier for the order.", + } + }, + "required": ["order_id"], + }, + }, + { + "name": "cancel_order", + "description": "Cancels an order based on the provided order ID.", + "input_schema": { + "type": "object", + "properties": { + "order_id": { + "type": "string", + "description": "The unique identifier for the order to be cancelled.", + } + }, + "required": ["order_id"], + }, + }, +] + +SYSTEM_PROMPT = """You are a customer service agent for an e-commerce platform. +Your role is to assist customers with their inquiries and issues. +You have access to tools to look up customer and order information, +and to process cancellations. Always be polite and helpful.""" + +def chatbot_interaction(user_message: str) -> str: + messages = [{"role": "user", "content": user_message}] + response = client.messages.create( + model=MODEL_NAME, max_tokens=4096, system=SYSTEM_PROMPT, + tools=tools, messages=messages, + ) + while response.stop_reason == "tool_use": + tool_use = next(b for b in response.content if b.type == "tool_use") + tool_result = process_tool_call(tool_use.name, tool_use.input) + messages = [ + {"role": "user", "content": user_message}, + {"role": "assistant", "content": response.content}, + {"role": "user", "content": [ + {"type": "tool_result", "tool_use_id": tool_use.id, "content": str(tool_result)} + ]}, + ] + response = client.messages.create( + model=MODEL_NAME, max_tokens=4096, system=SYSTEM_PROMPT, + tools=tools, messages=messages, + ) + return next(b.text for b in response.content if hasattr(b, "text"))`; + +const agentYaml = `spec_version: 0.1.0 +name: customer-service-agent +version: 0.1.0 +description: Customer service agent for an e-commerce platform +model: + preferred: claude-opus-4-1 +tools: + - get-customer-info + - get-order-details + - cancel-order`; + +const soulMd = `# Soul + +## Core Identity +You are a customer service agent for an e-commerce platform. + +## Purpose +Assist customers with their inquiries and issues. Look up customer and +order information, and process cancellations when requested. + +## Behavior +- Always be polite and helpful +- Use tools to look up accurate information before responding +- Confirm order details before processing a cancellation`; + +const toolGetCustomer = `name: get-customer-info +description: Retrieves customer information based on their customer ID. +input_schema: + type: object + properties: + customer_id: + type: string + description: The unique identifier for the customer + required: + - customer_id +implementation: + type: script + path: tools/get_customer_info.py + runtime: python3 + timeout: 30`; + +const toolGetOrder = `name: get-order-details +description: Retrieves the details of a specific order based on the order ID. +input_schema: + type: object + properties: + order_id: + type: string + description: The unique identifier for the order + required: + - order_id +implementation: + type: script + path: tools/get_order_details.py + runtime: python3 + timeout: 30`; + +const toolCancelOrder = `name: cancel-order +description: Cancels an order based on the provided order ID. +input_schema: + type: object + properties: + order_id: + type: string + description: The unique identifier for the order to be cancelled + required: + - order_id +implementation: + type: script + path: tools/cancel_order.py + runtime: python3 + timeout: 30`; + +const validateCmd = `opengap validate -d ./customer-service-opengap +opengap info -d ./customer-service-opengap`; + +const mapping = [ + ["SYSTEM_PROMPT passed to messages.create(system=...)", "SOUL.md"], + ["MODEL_NAME ('claude-opus-4-1')", "agent.yaml → model.preferred"], + ["tools[].name (kebab-case)", "agent.yaml → tools[]"], + ["tools[].description + input_schema", "tools/.yaml"], + ["process_tool_call() dispatch function", "stays in framework — tool implementation"], + ["chatbot_interaction() loop / multi-turn logic", "stays in framework — runtime loop"], +]; + +const steps = [ + { step: "1", desc: "Copy SYSTEM_PROMPT into SOUL.md. Keep identity, purpose, and behavioral rules together — the Claude SDK has a single flat prompt with no separate rules block." }, + { step: "2", desc: "Take MODEL_NAME → write to agent.yaml → model.preferred (e.g. claude-opus-4-1)." }, + { step: "3", desc: "For each entry in tools[], add a kebab-case name to agent.yaml → tools. get_customer_info → get-customer-info." }, + { step: "4", desc: "Create tools/.yaml for each tool — copy the name (kebab-case), description, and input_schema directly from the tool dict." }, + { step: "5", desc: "The process_tool_call() dispatch and the multi-turn chatbot_interaction() loop stay in the notebook — they are runtime execution, not agent identity." }, + { step: "6", desc: "Run opengap validate to confirm the structure is correct." }, +]; + +export function CookbookClaudeSDK() { + return ( +
+
+ + +

OpenGAP / Cookbook /

+

Claude SDK → OpenGAP

+

+ Based on anthropics/anthropic-cookbook customer service agent — a multi-turn + agent with three tools (get_customer_info, get_order_details, cancel_order) + and a system prompt. The Anthropic SDK has no built-in agent format — everything lives as function arguments. + Converting to OpenGAP means pulling those values into files. +

+
+ + {/* Part 1 */} + +

Part 1 — The Claude SDK agent

+

E-commerce customer service with three tools and a multi-turn loop:

+
+ + +
+
+ + {/* Part 2 */} + +

Part 2 — What maps to OpenGAP

+
+
+ Claude SDKOpenGAP +
+ {mapping.map(([from, to], i) => ( +
+ {from} + {to} +
+ ))} +
+
+ + {/* Part 3 */} + +

Part 3 — Create the OpenGAP files

+
+
+

agent.yaml:

+ +
+
+

SOUL.md — from SYSTEM_PROMPT:

+ +
+
+

tools/get-customer-info.yaml:

+ +
+
+

tools/get-order-details.yaml:

+ +
+
+

tools/cancel-order.yaml:

+ +
+
+
+ + {/* Part 4 */} + +

Part 4 — Validate

+ +
+ + {/* Steps */} + +

What happens step by step

+
+ {steps.map((s, i) => ( + +
+ {s.step} +

{s.desc}

+
+
+ ))} +
+
+ +
+
+ ); +} diff --git a/src/components/opengap/cookbook/CookbookCodex.tsx b/src/components/opengap/cookbook/CookbookCodex.tsx new file mode 100644 index 0000000..1bc12db --- /dev/null +++ b/src/components/opengap/cookbook/CookbookCodex.tsx @@ -0,0 +1,145 @@ +import { motion } from "framer-motion"; +import { CodeBlock } from "@/components/gitAgent/CodeBlock"; + +const projectStructure = `my-project/ +└── AGENTS.md ← identity, rules, and tool declarations`; + +const agentsMd = `# AGENTS.md (example) +You are a backend API developer specializing in Node.js and Express. +Help users build production-ready REST APIs with proper validation and error handling. + +Always validate inputs using Zod schemas. +Always return consistent JSON error responses. +Never expose stack traces in production error responses. + +## Tools +- run_tests: Run the test suite +- lint_code: Lint the codebase with ESLint`; + +const agentYaml = `spec_version: 0.1.0 +name: backend-api-agent +version: 0.1.0 +description: Backend API developer specializing in Node.js and Express +model: + preferred: gpt-4o +tools: + - run-tests + - lint-code`; + +const soulMd = `# Soul + +## Core Identity +You are a backend API developer specializing in Node.js and Express. + +## Purpose +Help users build production-ready REST APIs with proper validation and error handling.`; + +const rulesMd = `# Rules + +- Always validate inputs using Zod schemas +- Always return consistent JSON error responses +- Never expose stack traces in production error responses`; + +const toolRunTests = `name: run-tests +description: Run the test suite for the project. +input_schema: + type: object + properties: {} +implementation: + type: script + path: tools/run_tests.py + runtime: python3 + timeout: 60`; + +const toolLintCode = `name: lint-code +description: Lint the codebase with ESLint and report issues. +input_schema: + type: object + properties: {} +implementation: + type: script + path: tools/lint_code.py + runtime: python3 + timeout: 30`; + +const mapping = [ + ["AGENTS.md — identity + purpose paragraph", "SOUL.md → Core Identity + Purpose"], + ["AGENTS.md — behavioral rules", "RULES.md"], + ["AGENTS.md → Tools section — each tool name (kebab-case)", "agent.yaml → tools[] + tools/.yaml"], +]; + +const steps = [ + { step: "1", desc: "Open AGENTS.md. Copy the identity and purpose paragraph into SOUL.md." }, + { step: "2", desc: "Extract rule lines into RULES.md as a bullet list." }, + { step: "3", desc: "For each tool listed under ## Tools, add a kebab-case entry to agent.yaml → tools and create tools/.yaml." }, + { step: "4", desc: "Run opengap validate to confirm the structure is correct." }, +]; + +export function CookbookCodex() { + return ( +
+
+ + +

OpenGAP / Cookbook /

+

Codex → OpenGAP

+

+ Codex stores agent configuration in AGENTS.md — identity, rules, and tool declarations in one file. + Converting to OpenGAP means splitting identity into SOUL.md, rules into + RULES.md, and each tool into its own tools/<name>.yaml. +

+
+ + +

Part 1 — The Codex project

+

Typical Codex workspace structure:

+
+ + +
+
+ + +

Part 2 — What maps to OpenGAP

+
+
+ CodexOpenGAP +
+ {mapping.map(([from, to], i) => ( +
+ {from} + {to} +
+ ))} +
+
+ + +

Part 3 — Create the OpenGAP files

+
+

agent.yaml:

+

SOUL.md:

+

RULES.md:

+

tools/run-tests.yaml:

+

tools/lint-code.yaml:

+
+
+ + +

What happens step by step

+
+ {steps.map((s, i) => ( + +
+ {s.step} +

{s.desc}

+
+
+ ))} +
+
+ +
+
+ ); +} diff --git a/src/components/opengap/cookbook/CookbookCrewAI.tsx b/src/components/opengap/cookbook/CookbookCrewAI.tsx new file mode 100644 index 0000000..47b6942 --- /dev/null +++ b/src/components/opengap/cookbook/CookbookCrewAI.tsx @@ -0,0 +1,235 @@ +import { motion } from "framer-motion"; +import { CodeBlock } from "@/components/gitAgent/CodeBlock"; + +const projectStructure = `stephenc222/example-crewai/ +├── financial_analysis.py ← Crew, Agent, Task definitions +├── requirements.txt +└── .env.example`; + +const financialPy = `# financial_analysis.py +from langchain_community.tools.yahoo_finance_news import YahooFinanceNewsTool +from crewai import Agent, Task, Crew +from dotenv import load_dotenv + +load_dotenv() + +yahoo_finance_news_tool = YahooFinanceNewsTool() + +financial_analyst = Agent( + role='Financial Analyst', + goal='Analyze current financial news to identify market trends and investment opportunities', + backstory="""You are an experienced financial analyst adept at interpreting +market data and news to forecast financial trends and advise on investment strategies.""", + verbose=True, + allow_delegation=False, + tools=[yahoo_finance_news_tool], +) + +communications_specialist = Agent( + role='Corporate Communications Specialist', + goal='Communicate financial insights and market trends to company stakeholders', + backstory="""As a communications specialist in a corporate setting, your expertise lies in +crafting clear and concise messages from complex financial data for stakeholders and the public.""", + verbose=True, + allow_delegation=True, +) + +task1 = Task( + description="""Review the latest financial news using the Yahoo Finance News Tool. +Identify key market trends and potential investment opportunities.""", + agent=financial_analyst, +) + +task2 = Task( + description="""Based on the financial analyst's report, prepare a press release for the company. +Highlight the identified market trends and investment opportunities.""", + agent=communications_specialist, +) + +crew = Crew( + agents=[financial_analyst, communications_specialist], + tasks=[task1, task2], + verbose=2, +) + +result = crew.kickoff()`; + +const agentYaml = `spec_version: 0.1.0 +name: financial-analyst +version: 0.1.0 +description: Analyze financial news to identify market trends and investment opportunities +model: + preferred: gpt-4o +tools: + - yahoo-finance-news +agents: + communications-specialist: + description: Communicate financial insights and market trends to company stakeholders + delegation: + mode: auto`; + +const soulMd = `# Soul + +## Core Identity +Financial Analyst + +## Background +You are an experienced financial analyst adept at interpreting market data +and news to forecast financial trends and advise on investment strategies. + +## Purpose +Analyze current financial news to identify market trends and investment opportunities.`; + +const subAgentYaml = `spec_version: 0.1.0 +name: communications-specialist +version: 0.1.0 +description: Communicate financial insights and market trends to company stakeholders +model: + preferred: gpt-4o`; + +const subSoulMd = `# Soul + +## Core Identity +Corporate Communications Specialist + +## Background +As a communications specialist in a corporate setting, your expertise lies in +crafting clear and concise messages from complex financial data for stakeholders and the public. + +## Purpose +Transform financial analysis into clear, stakeholder-ready communications.`; + +const toolYaml = `name: yahoo-finance-news +description: Fetch the latest financial news from Yahoo Finance for a given ticker or topic. +input_schema: + type: object + properties: + query: + type: string + description: Stock ticker or financial topic to search for + required: + - query +implementation: + type: script + path: tools/yahoo_finance_news.py + runtime: python3 + timeout: 30`; + +const validateCmd = `opengap validate -d ./financial-analyst-opengap +opengap info -d ./financial-analyst-opengap`; + +const mapping = [ + ["Agent.role", "SOUL.md → ## Core Identity"], + ["Agent.backstory", "SOUL.md → ## Background"], + ["Agent.goal", "SOUL.md → ## Purpose + agent.yaml → description"], + ["Agent.tools (tool names, kebab-case)", "agent.yaml → tools[]"], + ["Each tool → name + description + schema", "tools/.yaml"], + ["First/primary agent", "root agent.yaml + SOUL.md"], + ["Additional agents", "agents//agent.yaml + SOUL.md"], + ["Task definitions", "stays in framework — no OpenGAP equivalent"], + ["Crew / Process type", "stays in framework — runtime orchestration"], +]; + +const steps = [ + { step: "1", desc: "Take the primary agent (financial_analyst) — its role, backstory, and goal map to SOUL.md ## Core Identity, ## Background, ## Purpose." }, + { step: "2", desc: "Write agent.yaml: use role as name (kebab-case), goal as description, and llm as model.preferred (defaults to gpt-4o if not set)." }, + { step: "3", desc: "For each tool in Agent.tools, add a kebab-case name to agent.yaml → tools. YahooFinanceNewsTool becomes yahoo-finance-news." }, + { step: "4", desc: "Create tools/.yaml for each tool with name, description, and input_schema." }, + { step: "5", desc: "For the second agent (communications_specialist), create agents/communications-specialist/agent.yaml and SOUL.md using the same mapping." }, + { step: "6", desc: "Task descriptions and Crew stay in financial_analysis.py — they are runtime coordination with no OpenGAP equivalent." }, +]; + +export function CookbookCrewAI() { + return ( +
+
+ + +

OpenGAP / Cookbook /

+

CrewAI → OpenGAP

+

+ Based on stephenc222/example-crewai — a financial analysis crew with + a Financial Analyst agent and a Communications Specialist agent working in sequence. + CrewAI's Agent(role, goal, backstory) maps cleanly to OpenGAP's + SOUL.md sections; each agent becomes its own OpenGAP directory. +

+
+ + {/* Part 1 */} + +

Part 1 — The CrewAI project

+

Financial analysis crew with Yahoo Finance news tool:

+
+ + +
+
+ + {/* Part 2 */} + +

Part 2 — What maps to OpenGAP

+
+
+ CrewAIOpenGAP +
+ {mapping.map(([from, to], i) => ( +
+ {from} + {to} +
+ ))} +
+
+ + {/* Part 3 */} + +

Part 3 — Create the OpenGAP files

+
+
+

Root agent: agent.yaml (from financial_analyst):

+ +
+
+

Root agent: SOUL.md:

+ +
+
+

tools/yahoo-finance-news.yaml — one file per tool:

+ +
+
+

Sub-agent: agents/communications-specialist/agent.yaml:

+ +
+
+

Sub-agent: agents/communications-specialist/SOUL.md:

+ +
+
+
+ + {/* Part 4 */} + +

Part 4 — Validate

+ +
+ + {/* Steps */} + +

What happens step by step

+
+ {steps.map((s, i) => ( + +
+ {s.step} +

{s.desc}

+
+
+ ))} +
+
+ +
+
+ ); +} diff --git a/src/components/opengap/cookbook/CookbookCursor.tsx b/src/components/opengap/cookbook/CookbookCursor.tsx new file mode 100644 index 0000000..95f1ce6 --- /dev/null +++ b/src/components/opengap/cookbook/CookbookCursor.tsx @@ -0,0 +1,115 @@ +import { motion } from "framer-motion"; +import { CodeBlock } from "@/components/gitAgent/CodeBlock"; + +const projectStructure = `my-project/ +├── .cursorrules ← identity and behavioral rules +└── .cursor/ + └── settings.json ← model config`; + +const cursorRules = `# .cursorrules (example) +You are an expert React and TypeScript developer. +Always use functional components and hooks. +Prefer Tailwind CSS over custom CSS files. +Never use class components. +Always add PropTypes or TypeScript interfaces for props. +Keep components small and focused on a single responsibility.`; + +const agentYaml = `spec_version: 0.1.0 +name: react-typescript-agent +version: 0.1.0 +description: Expert React and TypeScript developer assistant +model: + preferred: claude-sonnet-4-6`; + +const soulMd = `# Soul + +## Core Identity +You are an expert React and TypeScript developer.`; + +const rulesMd = `# Rules + +- Always use functional components and hooks +- Prefer Tailwind CSS over custom CSS files +- Never use class components +- Always add TypeScript interfaces for props +- Keep components small and focused on a single responsibility`; + +const mapping = [ + [".cursorrules — identity line", "SOUL.md → Core Identity"], + [".cursorrules — behavioral rules", "RULES.md"], + [".cursor/settings.json → model", "agent.yaml → model.preferred"], +]; + +const steps = [ + { step: "1", desc: "Open .cursorrules. The first line is usually the identity — copy it into SOUL.md under Core Identity." }, + { step: "2", desc: "Extract all rule lines (\"always\", \"never\", \"prefer\") into RULES.md as a bullet list." }, + { step: "3", desc: "Check .cursor/settings.json for a model setting. Write it to agent.yaml → model.preferred." }, + { step: "4", desc: "Run opengap validate to confirm the structure is correct." }, +]; + +export function CookbookCursor() { + return ( +
+
+ + +

OpenGAP / Cookbook /

+

Cursor → OpenGAP

+

+ Cursor stores agent behavior in .cursorrules — a flat file mixing identity and rules. + Converting to OpenGAP means splitting it into SOUL.md (who the agent is) + and RULES.md (how it behaves). +

+
+ + +

Part 1 — The Cursor project

+

Typical Cursor workspace structure:

+
+ + +
+
+ + +

Part 2 — What maps to OpenGAP

+
+
+ CursorOpenGAP +
+ {mapping.map(([from, to], i) => ( +
+ {from} + {to} +
+ ))} +
+
+ + +

Part 3 — Create the OpenGAP files

+
+

agent.yaml:

+

SOUL.md:

+

RULES.md:

+
+
+ + +

What happens step by step

+
+ {steps.map((s, i) => ( + +
+ {s.step} +

{s.desc}

+
+
+ ))} +
+
+ +
+
+ ); +} diff --git a/src/components/opengap/cookbook/CookbookGeminiCLI.tsx b/src/components/opengap/cookbook/CookbookGeminiCLI.tsx new file mode 100644 index 0000000..bb94492 --- /dev/null +++ b/src/components/opengap/cookbook/CookbookGeminiCLI.tsx @@ -0,0 +1,114 @@ +import { motion } from "framer-motion"; +import { CodeBlock } from "@/components/gitAgent/CodeBlock"; + +const projectStructure = `my-project/ +└── GEMINI.md ← identity, purpose, and behavioral rules`; + +const geminiMd = `# GEMINI.md (example) +You are a data analysis assistant specializing in Python. +Help users explore datasets, build visualizations, and write clean analysis scripts. + +Always explain your reasoning step by step before writing code. +Use pandas and matplotlib unless the user specifies otherwise. +Never modify the original dataset — always work on a copy.`; + +const agentYaml = `spec_version: 0.1.0 +name: data-analysis-agent +version: 0.1.0 +description: Data analysis assistant specializing in Python +model: + preferred: gemini-2.0-flash`; + +const soulMd = `# Soul + +## Core Identity +You are a data analysis assistant specializing in Python. + +## Purpose +Help users explore datasets, build visualizations, and write clean analysis scripts.`; + +const rulesMd = `# Rules + +- Always explain reasoning step by step before writing code +- Use pandas and matplotlib unless the user specifies otherwise +- Never modify the original dataset — always work on a copy`; + +const mapping = [ + ["GEMINI.md — identity + purpose paragraph", "SOUL.md → Core Identity + Purpose"], + ["GEMINI.md — behavioral rules", "RULES.md"], + ["Gemini model (e.g. gemini-2.0-flash)", "agent.yaml → model.preferred"], +]; + +const steps = [ + { step: "1", desc: "Open GEMINI.md. Copy the identity and purpose paragraph into SOUL.md." }, + { step: "2", desc: "Extract rule lines (\"always\", \"never\", \"use X unless...\") into RULES.md." }, + { step: "3", desc: "Set agent.yaml → model.preferred to the Gemini model being used (e.g. gemini-2.0-flash)." }, + { step: "4", desc: "Run opengap validate to confirm the structure is correct." }, +]; + +export function CookbookGeminiCLI() { + return ( +
+
+ + +

OpenGAP / Cookbook /

+

Gemini CLI → OpenGAP

+

+ Gemini CLI stores agent configuration in GEMINI.md — a single markdown file + with identity and rules together. Converting to OpenGAP means separating identity into + SOUL.md and rules into RULES.md. +

+
+ + +

Part 1 — The Gemini CLI project

+

Typical Gemini CLI workspace structure:

+
+ + +
+
+ + +

Part 2 — What maps to OpenGAP

+
+
+ Gemini CLIOpenGAP +
+ {mapping.map(([from, to], i) => ( +
+ {from} + {to} +
+ ))} +
+
+ + +

Part 3 — Create the OpenGAP files

+
+

agent.yaml:

+

SOUL.md:

+

RULES.md:

+
+
+ + +

What happens step by step

+
+ {steps.map((s, i) => ( + +
+ {s.step} +

{s.desc}

+
+
+ ))} +
+
+ +
+
+ ); +} diff --git a/src/components/opengap/cookbook/CookbookGoogleADK.tsx b/src/components/opengap/cookbook/CookbookGoogleADK.tsx new file mode 100644 index 0000000..e38a185 --- /dev/null +++ b/src/components/opengap/cookbook/CookbookGoogleADK.tsx @@ -0,0 +1,340 @@ +import { motion } from "framer-motion"; +import { CodeBlock } from "@/components/gitAgent/CodeBlock"; + +const projectStructure = `google/adk-samples/python/agents/travel-concierge/ +├── travel_concierge/ +│ ├── agent.py ← root agent (coordinates sub-agents) +│ ├── sub_agents/ +│ │ ├── inspiration/ +│ │ │ ├── agent.py ← place_agent + poi_agent + inspiration_agent +│ │ │ └── prompt.py ← INSPIRATION_AGENT_INSTR +│ │ ├── planning/agent.py +│ │ ├── booking/agent.py +│ │ └── ... +│ ├── tools/places.py ← Google Maps toolset +│ └── shared_libraries/types.py ← DestinationIdeas, POISuggestions schemas +└── pyproject.toml`; + +const agentPy = `# travel_concierge/agent.py +from google.adk.agents import Agent +from travel_concierge import MODEL +from travel_concierge.sub_agents.inspiration.agent import inspiration_agent +from travel_concierge.sub_agents.planning.agent import planning_agent +from travel_concierge.sub_agents.booking.agent import booking_agent + +root_agent = Agent( + name="travel_concierge", + model=MODEL, # e.g. "gemini-2.0-flash" + instruction="A Travel Concierge using the services of multiple sub-agents", + sub_agents=[ + inspiration_agent, + planning_agent, + booking_agent, + # + pre_trip, in_trip, post_trip + ], +)`; + +const inspirationPy = `# travel_concierge/sub_agents/inspiration/agent.py +from google.adk.agents import Agent +from google.adk.tools.agent_tool import AgentTool +from travel_concierge.tools.places import get_places_toolset +from travel_concierge.sub_agents.inspiration import prompt + +place_agent = Agent( + model=MODEL, + name="place_agent", + instruction=prompt.PLACE_AGENT_INSTR, + description="Suggests destinations given user preferences", +) + +poi_agent = Agent( + model=MODEL, + name="poi_agent", + description="Suggests activities and points of interest for a destination", + instruction=prompt.POI_AGENT_INSTR, + tools=[get_places_toolset()], # Google Maps grounding +) + +inspiration_agent = Agent( + model=MODEL, + name="inspiration_agent", + description="Travel inspiration: inspires users and discovers their next vacation", + instruction=prompt.INSPIRATION_AGENT_INSTR, + tools=[AgentTool(agent=place_agent), AgentTool(agent=poi_agent)], +)`; + +const promptPy = `# travel_concierge/sub_agents/inspiration/prompt.py (excerpt) +INSPIRATION_AGENT_INSTR = """ +You are a travel inspiration agent who helps users find their next dream vacation. +Your role is to help the user identify a destination and activities they're interested in. + +- Call place_agent to recommend vacation destinations given vague ideas. +- Call poi_agent to suggest points of interest once the user has a specific city in mind. +- Avoid asking too many questions — when user says "inspire me", call place_agent immediately. +- Transfer to planning_agent once the user wants a detailed itinerary or flight/hotel deals. + +Current user: + {user_profile} +Current time: {_time} +"""`; + +const agentYaml = `spec_version: 0.1.0 +name: travel-concierge +version: 0.1.0 +description: A travel concierge that coordinates specialized sub-agents across the full trip lifecycle +model: + preferred: gemini-2.0-flash +agents: + inspiration-agent: + description: Inspires users and discovers their next vacation destination + delegation: + mode: auto + planning-agent: + description: Creates detailed itineraries and schedules + delegation: + mode: auto + booking-agent: + description: Books flights and hotels + delegation: + mode: auto + pre-trip-agent: + description: Prepares travellers before departure + delegation: + mode: auto + in-trip-agent: + description: Provides real-time support during the trip + delegation: + mode: auto + post-trip-agent: + description: Handles post-trip feedback and follow-up + delegation: + mode: auto`; + +const soulMd = `# Soul + +## Core Identity +A Travel Concierge that coordinates multiple specialized sub-agents. + +## Purpose +Help users plan their entire trip — from initial inspiration and destination +discovery through booking, pre-trip preparation, and in-trip support. + +## Behavior +- Delegate to specialist sub-agents rather than handling tasks directly +- Hand off to inspiration_agent for destination ideas +- Hand off to planning_agent for itineraries and schedules +- Hand off to booking_agent for flights and hotels`; + +const subInspirationYaml = `spec_version: 0.1.0 +name: inspiration-agent +version: 0.1.0 +description: Travel inspiration — inspires users and discovers their next vacation destination +model: + preferred: gemini-2.0-flash +tools: + - place-agent + - poi-agent + - google-places`; + +const subInspirationSoul = `# Soul + +## Core Identity +You are a travel inspiration agent who helps users find their next dream vacation. + +## Purpose +Help the user identify a destination and activities they're interested in. + +## Behavior +- Call place_agent to recommend vacation destinations given vague ideas +- Call poi_agent to suggest points of interest once a city is selected +- When user says "inspire me", act immediately — don't ask questions first +- Transfer to planning_agent once user wants a detailed itinerary`; + +const toolPlaceAgent = `name: place-agent +description: Suggests vacation destinations given user preferences and vague ideas. +input_schema: + type: object + properties: + inspiration_query: + type: string + description: User preferences or vague idea for a destination + required: + - inspiration_query +implementation: + type: script + path: tools/place_agent.py + runtime: python3 + timeout: 30`; + +const toolPoiAgent = `name: poi-agent +description: Suggests activities and points of interest for a specific destination. +input_schema: + type: object + properties: + destination: + type: string + description: The city or destination to find activities for + required: + - destination +implementation: + type: script + path: tools/poi_agent.py + runtime: python3 + timeout: 30`; + +const toolGooglePlaces = `name: google-places +description: Search Google Maps for places, attractions, and local businesses near a location. +input_schema: + type: object + properties: + query: + type: string + description: Search query for places or attractions + location: + type: string + description: The city or area to search within + required: + - query + - location +implementation: + type: script + path: tools/google_places.py + runtime: python3 + timeout: 30`; + +const validateCmd = `opengap validate -d ./travel-concierge-opengap +opengap info -d ./travel-concierge-opengap`; + +const mapping = [ + ["Agent(instruction=...)", "SOUL.md"], + ["Agent(name=...) — kebab-case", "agent.yaml → name"], + ["Agent(model=...) e.g. gemini-2.0-flash", "agent.yaml → model.preferred"], + ["Agent(description=...)", "agent.yaml → description"], + ["Agent(sub_agents=[...]) — each sub-agent", "agents//agent.yaml + SOUL.md"], + ["AgentTool(agent=X) — delegated sub-agents as tools", "agent.yaml → tools[] + tools/.yaml"], + ["FunctionTool / get_places_toolset()", "tools/.yaml"], + ["prompt.py instruction constants", "sub-agent SOUL.md"], +]; + +const steps = [ + { step: "1", desc: "Start with the root agent. Copy Agent(instruction=...) into SOUL.md. Use Agent(name=...) as agent.yaml → name (kebab-case)." }, + { step: "2", desc: "Take Agent(model=...) (the MODEL constant, e.g. gemini-2.0-flash) → write to agent.yaml → model.preferred." }, + { step: "3", desc: "For each Agent in sub_agents=[], create a directory agents// with its own agent.yaml and SOUL.md using the same mapping." }, + { step: "4", desc: "For each AgentTool(agent=X) used as a tool inside a sub-agent, add a kebab-case entry to that agent's tools[] and create tools/.yaml." }, + { step: "5", desc: "For each FunctionTool or toolset (e.g. Google Maps grounding), create tools/.yaml with description and input_schema." }, + { step: "6", desc: "Prompt constants in prompt.py become each sub-agent's SOUL.md. Template variables like {user_profile} can be noted in SOUL.md comments." }, +]; + +export function CookbookGoogleADK() { + return ( +
+
+ + +

OpenGAP / Cookbook /

+

Google ADK → OpenGAP

+

+ Based on google/adk-samples travel concierge — a root agent that coordinates + six specialized sub-agents (inspiration, planning, booking, pre-trip, in-trip, post-trip). + Each Agent maps to its own OpenGAP directory; + AgentTool sub-agents become tool entries. +

+
+ + {/* Part 1 */} + +

Part 1 — The Google ADK project

+

Multi-agent travel concierge with sub-agents across the trip lifecycle:

+
+ +
+

travel_concierge/agent.py — root agent:

+ +
+
+

sub_agents/inspiration/agent.py — example sub-agent:

+ +
+
+

sub_agents/inspiration/prompt.py:

+ +
+
+
+ + {/* Part 2 */} + +

Part 2 — What maps to OpenGAP

+
+
+ Google ADKOpenGAP +
+ {mapping.map(([from, to], i) => ( +
+ {from} + {to} +
+ ))} +
+
+ + {/* Part 3 */} + +

Part 3 — Create the OpenGAP files

+
+
+

Root: agent.yaml:

+ +
+
+

Root: SOUL.md:

+ +
+
+

Sub-agent: agents/inspiration-agent/agent.yaml:

+ +
+
+

Sub-agent: agents/inspiration-agent/SOUL.md — from prompt.py:

+ +
+
+

Sub-agent tool: agents/inspiration-agent/tools/place-agent.yaml — AgentTool mapped to tool YAML:

+ +
+
+

Sub-agent tool: agents/inspiration-agent/tools/poi-agent.yaml:

+ +
+
+

Sub-agent tool: agents/inspiration-agent/tools/google-places.yaml — from get_places_toolset():

+ +
+
+
+ + {/* Part 4 */} + +

Part 4 — Validate

+ +
+ + {/* Steps */} + +

What happens step by step

+
+ {steps.map((s, i) => ( + +
+ {s.step} +

{s.desc}

+
+
+ ))} +
+
+ +
+
+ ); +} diff --git a/src/components/opengap/cookbook/CookbookLangChain.tsx b/src/components/opengap/cookbook/CookbookLangChain.tsx new file mode 100644 index 0000000..cabe534 --- /dev/null +++ b/src/components/opengap/cookbook/CookbookLangChain.tsx @@ -0,0 +1,232 @@ +import { motion } from "framer-motion"; +import { CodeBlock } from "@/components/gitAgent/CodeBlock"; + +const projectStructure = `my-langchain-agent/ +├── agent.py ← AgentExecutor, prompt, model, tools +├── tools.py ← @tool decorated functions +└── requirements.txt`; + +const agentPy = `# agent.py +from langchain.agents import AgentExecutor, create_tool_calling_agent +from langchain_anthropic import ChatAnthropic +from langchain_core.prompts import ChatPromptTemplate +from tools import get_order_status, initiate_return + +SYSTEM_PROMPT = """You are a customer support agent for an e-commerce platform. +Help customers with order status and return requests. +Always verify the order ID before discussing order details. + +Rules: +- Never share one customer's data with another +- Always confirm before initiating a return""" + +prompt = ChatPromptTemplate.from_messages([ + ("system", SYSTEM_PROMPT), + ("human", "{input}"), + ("placeholder", "{agent_scratchpad}"), +]) + +model = ChatAnthropic(model="claude-sonnet-4-6") +tools = [get_order_status, initiate_return] + +agent = create_tool_calling_agent(model, tools, prompt) +agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)`; + +const toolsPy = `# tools.py +from langchain_core.tools import tool + +@tool +def get_order_status(order_id: str) -> str: + """Get the current status of a customer order by order ID.""" + # real impl queries your order database + return f"Order {order_id} is being processed." + +@tool +def initiate_return(order_id: str, reason: str) -> str: + """Initiate a return request for an order. + + Args: + order_id: The order to return. + reason: The reason for the return. + """ + return f"Return initiated for order {order_id}. Reason: {reason}."`; + +const agentYaml = `spec_version: 0.1.0 +name: customer-support-agent +version: 0.1.0 +description: Customer support agent for an e-commerce platform +model: + preferred: claude-sonnet-4-6 +tools: + - get-order-status + - initiate-return`; + +const soulMd = `# Soul + +## Core Identity +You are a customer support agent for an e-commerce platform. + +## Purpose +Help customers with order status and return requests. +Always verify the order ID before discussing order details.`; + +const rulesMd = `# Rules + +- Never share one customer's data with another +- Always confirm before initiating a return`; + +const toolGetOrder = `name: get-order-status +description: Get the current status of a customer order by order ID. +input_schema: + type: object + properties: + order_id: + type: string + description: The order ID to look up + required: + - order_id +implementation: + type: script + path: tools/get_order_status.py + runtime: python3 + timeout: 30`; + +const toolReturn = `name: initiate-return +description: Initiate a return request for an order. +input_schema: + type: object + properties: + order_id: + type: string + description: The order to return + reason: + type: string + description: The reason for the return + required: + - order_id + - reason +implementation: + type: script + path: tools/initiate_return.py + runtime: python3 + timeout: 30`; + +const validateCmd = `opengap validate -d ./customer-support-opengap +opengap info -d ./customer-support-opengap`; + +const mapping = [ + ["SYSTEM_PROMPT — identity/purpose lines", "SOUL.md"], + ["SYSTEM_PROMPT — Rules: section", "RULES.md"], + ["ChatAnthropic(model=...)", "agent.yaml → model.preferred"], + ["@tool function names (kebab-case)", "agent.yaml → tools[]"], + ["@tool docstring + typed args", "tools/.yaml description + input_schema"], + ["Existing tools.py function", "tools/.yaml → implementation.path"], + ["AgentExecutor + ChatPromptTemplate", "stays in framework — runtime execution loop"], + ["agent_scratchpad placeholder", "stays in framework — internal reasoning trace"], +]; + +const steps = [ + { step: "1", desc: "Split SYSTEM_PROMPT by content: who-you-are and purpose lines → SOUL.md. Hard rules (never, always) → RULES.md." }, + { step: "2", desc: "Take the model string from ChatAnthropic(model=...) → write to agent.yaml → model.preferred." }, + { step: "3", desc: "List each @tool function name → add to agent.yaml → tools as kebab-case (get_order_status → get-order-status)." }, + { step: "4", desc: "Create tools/.yaml for each tool — copy the docstring as description, typed args as input_schema, and point implementation.path to the existing Python file." }, + { step: "5", desc: "AgentExecutor, ChatPromptTemplate, and the agent_scratchpad placeholder stay in agent.py — they are LangChain runtime wiring." }, + { step: "6", desc: "Run opengap validate to confirm the structure is correct." }, +]; + +export function CookbookLangChain() { + return ( +
+
+ + +

OpenGAP / Cookbook /

+

LangChain → OpenGAP

+

+ LangChain agents use AgentExecutor, a ChatPromptTemplate, + and @tool decorated functions. The system prompt is a plain string + inside the template. Converting to OpenGAP means pulling the prompt into files and declaring the tools — + the executor and template stay in your code. +

+
+ + +

Part 1 — The LangChain project

+

Customer support agent with two tools:

+
+ +
+

tools.py:

+ +
+
+

agent.py:

+ +
+
+
+ + +

Part 2 — What maps to OpenGAP

+
+
+ LangChainOpenGAP +
+ {mapping.map(([from, to], i) => ( +
+ {from} + {to} +
+ ))} +
+
+ + +

Part 3 — Create the OpenGAP files

+
+
+

agent.yaml:

+ +
+
+

SOUL.md — identity lines from SYSTEM_PROMPT:

+ +
+
+

RULES.md — hard rules from SYSTEM_PROMPT:

+ +
+
+

tools/get-order-status.yaml:

+ +
+
+

tools/initiate-return.yaml:

+ +
+
+
+ + +

Part 4 — Validate

+ +
+ + +

What happens step by step

+
+ {steps.map((s, i) => ( + +
+ {s.step} +

{s.desc}

+
+
+ ))} +
+
+ +
+
+ ); +} diff --git a/src/components/opengap/cookbook/CookbookLangGraph.tsx b/src/components/opengap/cookbook/CookbookLangGraph.tsx new file mode 100644 index 0000000..8203318 --- /dev/null +++ b/src/components/opengap/cookbook/CookbookLangGraph.tsx @@ -0,0 +1,202 @@ +import { motion } from "framer-motion"; +import { CodeBlock } from "@/components/gitAgent/CodeBlock"; + +const projectStructure = `langchain-ai/react-agent/ +├── src/react_agent/ +│ ├── graph.py ← StateGraph: nodes, edges, routing +│ ├── tools.py ← TOOLS list (Tavily search) +│ ├── prompts.py ← SYSTEM_PROMPT string +│ ├── state.py ← State, InputState TypedDicts +│ └── configuration.py← model + max_search_results config +└── langgraph.json ← graph entry point for Studio`; + +const promptsPy = `# src/react_agent/prompts.py +"""Default prompts used by the agent.""" + +SYSTEM_PROMPT = """You are a helpful AI assistant. + +System time: {system_time}"""`; + +const graphPy = `# src/react_agent/graph.py (key excerpt) +from react_agent.prompts import SYSTEM_PROMPT +from react_agent.tools import TOOLS +from react_agent.utils import load_chat_model + +async def call_model(state: State, runtime: Runtime[Context]): + model = load_chat_model(runtime.context.model).bind_tools(TOOLS) + system_message = runtime.context.system_prompt.format( + system_time=datetime.now(tz=UTC).isoformat() + ) + response = await model.ainvoke( + [{"role": "system", "content": system_message}, *state.messages] + ) + return {"messages": [response]} + +builder = StateGraph(State, input_schema=InputState, context_schema=Context) +builder.add_node(call_model) +builder.add_node("tools", ToolNode(TOOLS)) +builder.add_edge("__start__", "call_model") + +def route_model_output(state: State): + last = state.messages[-1] + return "__end__" if not last.tool_calls else "tools" + +builder.add_conditional_edges("call_model", route_model_output) +builder.add_edge("tools", "call_model") +graph = builder.compile(name="ReAct Agent")`; + +const toolsPy = `# src/react_agent/tools.py +from langchain_community.tools.tavily_search import TavilySearchResults +from langgraph.runtime import Runtime +from react_agent.context import Context + +async def search(query: str, runtime: Runtime[Context]) -> list[dict]: + """Search the web using Tavily.""" + max_results = runtime.context.max_search_results + wrapped = TavilySearchResults(max_results=max_results) + return await wrapped.ainvoke({"query": query}) + +TOOLS = [search]`; + +const agentYaml = `spec_version: 0.1.0 +name: react-agent +version: 0.1.0 +description: ReAct agent that searches the web to answer questions accurately +model: + preferred: claude-sonnet-4-6 +tools: + - search`; + +const soulMd = `# Soul + +## Core Identity +You are a helpful AI assistant.`; + +const toolYaml = `name: search +description: Search the web using Tavily for up-to-date information. +input_schema: + type: object + properties: + query: + type: string + description: The search query + required: + - query +implementation: + type: script + path: tools/search.py + runtime: python3 + timeout: 30`; + +const validateCmd = `opengap validate -d ./react-agent-opengap +opengap info -d ./react-agent-opengap`; + +const mapping = [ + ["SYSTEM_PROMPT in prompts.py", "SOUL.md"], + ["runtime.context.model (from configuration.py)", "agent.yaml → model.preferred"], + ["TOOLS list — function names", "agent.yaml → tools[]"], + ["Each tool function + docstring + typed args", "tools/.yaml"], + ["implementation.path → existing tools.py function", "tools/.yaml → implementation.path"], + ["StateGraph nodes + edges in graph.py", "stays in framework — runtime orchestration"], +]; + +const steps = [ + { step: "1", desc: "Copy SYSTEM_PROMPT from prompts.py into SOUL.md." }, + { step: "2", desc: "Take the model name from configuration.py → write to agent.yaml → model.preferred." }, + { step: "3", desc: "For each function in the TOOLS list, add a kebab-case name to agent.yaml → tools (search stays search, web_search becomes web-search)." }, + { step: "4", desc: "Create tools/.yaml for each tool — use the docstring as description, typed parameters as input_schema, and point implementation.path to your existing tool file." }, + { step: "5", desc: "StateGraph, ToolNode, routing logic, and State TypedDicts stay in graph.py — they are runtime execution wiring with no OpenGAP equivalent." }, + { step: "6", desc: "Run opengap validate to confirm the structure is correct." }, +]; + +export function CookbookLangGraph() { + return ( +
+
+ + +

OpenGAP / Cookbook /

+

LangGraph → OpenGAP

+

+ Based on langchain-ai/react-agent — the official LangGraph ReAct agent template. + The agent identity lives in prompts.py, tools are exported from tools.py, + and the graph wiring in graph.py stays in the framework. +

+
+ + +

Part 1 — The LangGraph project

+

ReAct agent with Tavily web search, designed for LangGraph Studio:

+
+ +
+

prompts.py:

+ +
+
+

tools.py:

+ +
+
+

graph.py excerpt:

+ +
+
+
+ + +

Part 2 — What maps to OpenGAP

+
+
+ LangGraph (react-agent)OpenGAP +
+ {mapping.map(([from, to], i) => ( +
+ {from} + {to} +
+ ))} +
+
+ + +

Part 3 — Create the OpenGAP files

+
+
+

agent.yaml:

+ +
+
+

SOUL.md — from SYSTEM_PROMPT:

+ +
+
+

tools/search.yaml — the implementation.path points to your existing tool file:

+ +
+
+
+ + +

Part 4 — Validate

+ +
+ + +

What happens step by step

+
+ {steps.map((s, i) => ( + +
+ {s.step} +

{s.desc}

+
+
+ ))} +
+
+ +
+
+ ); +} diff --git a/src/components/opengap/cookbook/CookbookOpenAIAgents.tsx b/src/components/opengap/cookbook/CookbookOpenAIAgents.tsx new file mode 100644 index 0000000..017cba3 --- /dev/null +++ b/src/components/opengap/cookbook/CookbookOpenAIAgents.tsx @@ -0,0 +1,311 @@ +import { motion } from "framer-motion"; +import { CodeBlock } from "@/components/gitAgent/CodeBlock"; + +const projectStructure = `openai/openai-agents-python/ +└── examples/customer_service/ + └── main.py ← triage_agent, faq_agent, seat_booking_agent + tools`; + +const mainPy = `# examples/customer_service/main.py (key excerpt) +from agents import Agent, function_tool, handoff, Runner +from pydantic import BaseModel + +class AirlineAgentContext(BaseModel): + passenger_name: str | None = None + confirmation_number: str | None = None + seat_number: str | None = None + flight_number: str | None = None + +@function_tool(name_override="faq_lookup_tool", + description_override="Lookup frequently asked questions.") +async def faq_lookup_tool(question: str) -> str: + question_lower = question.lower() + if any(k in question_lower for k in ["bag", "baggage", "luggage"]): + return "One bag allowed, under 50 lbs and 22x14x9 inches." + elif "seat" in question_lower: + return "120 seats: 22 business, 98 economy. Exit rows 4 and 16." + elif "wifi" in question_lower: + return "Free wifi — join Airline-Wifi" + return "I'm sorry, I don't know the answer to that question." + +@function_tool +async def update_seat( + context: RunContextWrapper[AirlineAgentContext], + confirmation_number: str, + new_seat: str, +) -> str: + """Update the seat for a given confirmation number.""" + context.context.confirmation_number = confirmation_number + context.context.seat_number = new_seat + return f"Updated seat to {new_seat} for {confirmation_number}" + +faq_agent = Agent[AirlineAgentContext]( + name="FAQ Agent", + handoff_description="Answers questions about the airline.", + instructions="""You are an FAQ agent. Use faq_lookup_tool to answer the customer's +question. If you cannot answer it, transfer back to the triage agent.""", + tools=[faq_lookup_tool], +) + +seat_booking_agent = Agent[AirlineAgentContext]( + name="Seat Booking Agent", + handoff_description="Updates a seat on a flight.", + instructions="""You are a seat booking agent. Ask for the confirmation number, +ask for the desired seat, then call update_seat.""", + tools=[update_seat], +) + +triage_agent = Agent[AirlineAgentContext]( + name="Triage Agent", + handoff_description="Routes customer requests to the appropriate agent.", + instructions="You are a helpful triaging agent. Delegate to FAQ or seat booking agents.", + handoffs=[ + handoff(agent=faq_agent, tool_name_override="transfer_to_faq_agent"), + handoff(agent=seat_booking_agent, tool_name_override="transfer_to_seat_booking_agent"), + ], +)`; + +const agentYaml = `spec_version: 0.1.0 +name: triage-agent +version: 0.1.0 +description: Routes airline customer requests to the appropriate specialist agent +model: + preferred: gpt-4o +tools: + - transfer-to-faq-agent + - transfer-to-seat-booking-agent +agents: + faq-agent: + description: Answers frequently asked questions about the airline + delegation: + mode: auto + seat-booking-agent: + description: Updates seat assignments for airline passengers + delegation: + mode: auto`; + +const soulMd = `# Soul + +## Core Identity +You are a helpful triaging agent for an airline customer service system. + +## Purpose +Evaluate customer requests and delegate to the FAQ agent or seat booking agent +using the appropriate transfer tool.`; + +const subFaqYaml = `spec_version: 0.1.0 +name: faq-agent +version: 0.1.0 +description: Answers frequently asked questions about the airline +model: + preferred: gpt-4o +tools: + - faq-lookup-tool`; + +const subFaqSoul = `# Soul + +## Core Identity +You are an FAQ agent for an airline customer service system. + +## Behavior +Use the faq_lookup_tool to answer each question. Do not rely on your own knowledge. +If you cannot answer, transfer back to the triage agent.`; + +const subSeatYaml = `spec_version: 0.1.0 +name: seat-booking-agent +version: 0.1.0 +description: Updates seat assignments for airline passengers +model: + preferred: gpt-4o +tools: + - update-seat`; + +const toolTransferFaq = `name: transfer-to-faq-agent +description: Transfer the customer to the FAQ agent. +input_schema: + type: object + properties: {} +implementation: + type: script + path: tools/transfer_to_faq_agent.py + runtime: python3 + timeout: 30`; + +const toolTransferSeat = `name: transfer-to-seat-booking-agent +description: Transfer the customer to the seat booking agent. +input_schema: + type: object + properties: {} +implementation: + type: script + path: tools/transfer_to_seat_booking_agent.py + runtime: python3 + timeout: 30`; + +const toolFaqLookup = `name: faq-lookup-tool +description: Lookup frequently asked questions about the airline. +input_schema: + type: object + properties: + question: + type: string + description: The customer's question + required: + - question +implementation: + type: script + path: tools/faq_lookup_tool.py + runtime: python3 + timeout: 30`; + +const toolUpdateSeat = `name: update-seat +description: Update the seat for a given confirmation number. +input_schema: + type: object + properties: + confirmation_number: + type: string + description: The booking confirmation number + new_seat: + type: string + description: The desired seat assignment + required: + - confirmation_number + - new_seat +implementation: + type: script + path: tools/update_seat.py + runtime: python3 + timeout: 30`; + +const validateCmd = `opengap validate -d ./triage-agent-opengap +opengap info -d ./triage-agent-opengap`; + +const mapping = [ + ["Agent.instructions", "SOUL.md"], + ["Agent.name (kebab-case)", "agent.yaml → name"], + ["Agent.model", "agent.yaml → model.preferred"], + ["@function_tool functions (kebab-case names)", "agent.yaml → tools[] + tools/.yaml"], + ["handoff(agent=X, tool_name_override=Y)", "agent.yaml → tools[] (transfer tools)"], + ["Each Agent in handoffs list", "agents//agent.yaml + SOUL.md"], + ["AirlineAgentContext Pydantic model", "stays in framework — runtime shared state"], + ["Runner.run() + conversation loop", "stays in framework — execution runner"], +]; + +const steps = [ + { step: "1", desc: "Identify the root/entry agent (triage_agent). Its instructions → SOUL.md. Its name → agent.yaml name (kebab-case)." }, + { step: "2", desc: "For each handoff, add a transfer tool entry to agent.yaml → tools (e.g. transfer_to_faq_agent → transfer-to-faq-agent) and create a minimal tools/.yaml." }, + { step: "3", desc: "For each agent in handoffs (faq_agent, seat_booking_agent), create agents//agent.yaml + SOUL.md using the same mapping." }, + { step: "4", desc: "For each @function_tool (faq_lookup_tool, update_seat), add to the relevant sub-agent's tools list and create tools/.yaml." }, + { step: "5", desc: "AirlineAgentContext, RunContextWrapper, on_handoff hooks, and Runner.run() stay in main.py — runtime execution and state." }, + { step: "6", desc: "Run opengap validate on each agent directory to confirm the structure is correct." }, +]; + +export function CookbookOpenAIAgents() { + return ( +
+
+ + +

OpenGAP / Cookbook /

+

OpenAI Agents SDK → OpenGAP

+

+ Based on openai/openai-agents-python customer service example — a triage agent + that routes airline customers to an FAQ agent or a seat booking agent via handoffs. + Each Agent maps to its own OpenGAP directory; handoffs become transfer tools. +

+
+ + {/* Part 1 */} + +

Part 1 — The OpenAI Agents SDK project

+

Airline customer service with triage + FAQ + seat booking agents:

+
+ + +
+
+ + {/* Part 2 */} + +

Part 2 — What maps to OpenGAP

+
+
+ OpenAI Agents SDKOpenGAP +
+ {mapping.map(([from, to], i) => ( +
+ {from} + {to} +
+ ))} +
+
+ + {/* Part 3 */} + +

Part 3 — Create the OpenGAP files

+
+
+

Root: agent.yaml (triage_agent):

+ +
+
+

Root: SOUL.md:

+ +
+
+

tools/transfer-to-faq-agent.yaml:

+ +
+
+

tools/transfer-to-seat-booking-agent.yaml:

+ +
+
+

Sub-agent: agents/faq-agent/agent.yaml:

+ +
+
+

Sub-agent: agents/faq-agent/SOUL.md:

+ +
+
+

Sub-agent: agents/faq-agent/tools/faq-lookup-tool.yaml:

+ +
+
+

Sub-agent: agents/seat-booking-agent/agent.yaml:

+ +
+
+

Sub-agent: agents/seat-booking-agent/tools/update-seat.yaml:

+ +
+
+
+ + {/* Part 4 */} + +

Part 4 — Validate

+ +
+ + {/* Steps */} + +

What happens step by step

+
+ {steps.map((s, i) => ( + +
+ {s.step} +

{s.desc}

+
+
+ ))} +
+
+ +
+
+ ); +} diff --git a/src/components/opengap/cookbook/CookbookOpenCode.tsx b/src/components/opengap/cookbook/CookbookOpenCode.tsx new file mode 100644 index 0000000..922d606 --- /dev/null +++ b/src/components/opengap/cookbook/CookbookOpenCode.tsx @@ -0,0 +1,119 @@ +import { motion } from "framer-motion"; +import { CodeBlock } from "@/components/gitAgent/CodeBlock"; + +const projectStructure = `my-project/ +└── .opencode/ + └── config.json ← model, system prompt, and tool config`; + +const configJson = `// .opencode/config.json (example) +{ + "model": "gpt-4o", + "system": "You are a full-stack developer assistant. Help users build production-ready applications. Always consider security and performance. Prefer simple, readable solutions over clever ones.", + "tools": { + "bash": { "enabled": true }, + "read": { "enabled": true }, + "write": { "enabled": true } + } +}`; + +const agentYaml = `spec_version: 0.1.0 +name: fullstack-dev-agent +version: 0.1.0 +description: Full-stack developer assistant for production-ready applications +model: + preferred: gpt-4o`; + +const soulMd = `# Soul + +## Core Identity +You are a full-stack developer assistant. + +## Purpose +Help users build production-ready applications.`; + +const rulesMd = `# Rules + +- Always consider security and performance +- Prefer simple, readable solutions over clever ones`; + +const mapping = [ + [".opencode/config.json → system", "SOUL.md + RULES.md"], + [".opencode/config.json → model", "agent.yaml → model.preferred"], + [".opencode/config.json → tools", "agent.yaml → tools[] (built-in tools stay in runtime)"], +]; + +const steps = [ + { step: "1", desc: "Open .opencode/config.json. Read the system field — it contains both identity and rules mixed together." }, + { step: "2", desc: "Split the system prompt: who the agent is and what it does → SOUL.md. Behavioral constraints → RULES.md." }, + { step: "3", desc: "Copy the model value from config.json → agent.yaml → model.preferred." }, + { step: "4", desc: "Built-in tools (bash, read, write) are runtime capabilities — they don't need tool YAML files." }, + { step: "5", desc: "Run opengap validate to confirm the structure is correct." }, +]; + +export function CookbookOpenCode() { + return ( +
+
+ + +

OpenGAP / Cookbook /

+

OpenCode → OpenGAP

+

+ OpenCode stores agent configuration in .opencode/config.json — model, system prompt, + and tool toggles in a single JSON file. Converting to OpenGAP means extracting the system prompt + into SOUL.md and RULES.md, and + the model into agent.yaml. +

+
+ + +

Part 1 — The OpenCode project

+

Typical OpenCode workspace structure:

+
+ + +
+
+ + +

Part 2 — What maps to OpenGAP

+
+
+ OpenCodeOpenGAP +
+ {mapping.map(([from, to], i) => ( +
+ {from} + {to} +
+ ))} +
+
+ + +

Part 3 — Create the OpenGAP files

+
+

agent.yaml:

+

SOUL.md:

+

RULES.md:

+
+
+ + +

What happens step by step

+
+ {steps.map((s, i) => ( + +
+ {s.step} +

{s.desc}

+
+
+ ))} +
+
+ +
+
+ ); +} diff --git a/src/pages/OpenGAPDocsPage.tsx b/src/pages/OpenGAPDocsPage.tsx index 11e5cb6..3c35de6 100644 --- a/src/pages/OpenGAPDocsPage.tsx +++ b/src/pages/OpenGAPDocsPage.tsx @@ -10,12 +10,24 @@ import { QuickStartSection } from "@/components/QuickStartSection"; import { WhySection } from "@/components/WhySection"; import { HowItWorksSection } from "@/components/HowItWorksSection"; import { CLISection } from "@/components/CLISection"; +import { ImportSection } from "@/components/ImportSection"; import { ExportSection } from "@/components/ExportSection"; import { AdaptersSection } from "@/components/AdaptersSection"; import { SkillsSection } from "@/components/SkillsSection"; import { SkillsFlowSection } from "@/components/SkillsFlowSection"; import { ComplianceSection } from "@/components/ComplianceSection"; import { FAQSection } from "@/components/FAQSection"; +import { CookbookLangGraph } from "@/components/opengap/cookbook/CookbookLangGraph"; +import { CookbookCrewAI } from "@/components/opengap/cookbook/CookbookCrewAI"; +import { CookbookAutoGen } from "@/components/opengap/cookbook/CookbookAutoGen"; +import { CookbookOpenAIAgents } from "@/components/opengap/cookbook/CookbookOpenAIAgents"; +import { CookbookClaudeSDK } from "@/components/opengap/cookbook/CookbookClaudeSDK"; +import { CookbookGoogleADK } from "@/components/opengap/cookbook/CookbookGoogleADK"; +import { CookbookClaudeCode } from "@/components/opengap/cookbook/CookbookClaudeCode"; +import { CookbookCursor } from "@/components/opengap/cookbook/CookbookCursor"; +import { CookbookGeminiCLI } from "@/components/opengap/cookbook/CookbookGeminiCLI"; +import { CookbookCodex } from "@/components/opengap/cookbook/CookbookCodex"; +import { CookbookOpenCode } from "@/components/opengap/cookbook/CookbookOpenCode"; import { Footer } from "@/components/Footer"; const SECTION_COMPONENTS: Record = { @@ -24,12 +36,24 @@ const SECTION_COMPONENTS: Record = { why: WhySection, "how-it-works": HowItWorksSection, cli: CLISection, + import: ImportSection, export: ExportSection, adapters: AdaptersSection, skills: SkillsSection, skillflow: SkillsFlowSection, compliance: ComplianceSection, faq: FAQSection, + "cookbook-langgraph": CookbookLangGraph, + "cookbook-crewai": CookbookCrewAI, + "cookbook-autogen": CookbookAutoGen, + "cookbook-openai-agents": CookbookOpenAIAgents, + "cookbook-claude-sdk": CookbookClaudeSDK, + "cookbook-google-adk": CookbookGoogleADK, + "cookbook-claude-code": CookbookClaudeCode, + "cookbook-cursor": CookbookCursor, + "cookbook-gemini-cli": CookbookGeminiCLI, + "cookbook-codex": CookbookCodex, + "cookbook-opencode": CookbookOpenCode, }; const ALL_ITEMS = opengapSidebarGroups.flatMap((g) => g.items); From 56ce594e8dcdb8d5e35d0e73a2d99776abe7a396 Mon Sep 17 00:00:00 2001 From: Amogh Sunil Date: Thu, 11 Jun 2026 18:11:41 +0530 Subject: [PATCH 2/8] Update import page, sidebar, navbar, and LangGraph cookbook with improved designs --- src/components/ImportSection.tsx | 26 +- src/components/opengap/OpenGAPNavbar.tsx | 31 +- src/components/opengap/OpenGAPSidebar.tsx | 160 ++- .../opengap/cookbook/CookbookLangGraph.tsx | 935 +++++++++++++++--- src/pages/OpenGAPDocsPage.tsx | 13 +- 5 files changed, 1019 insertions(+), 146 deletions(-) diff --git a/src/components/ImportSection.tsx b/src/components/ImportSection.tsx index 4fa454d..285ab12 100644 --- a/src/components/ImportSection.tsx +++ b/src/components/ImportSection.tsx @@ -5,7 +5,7 @@ import { useState } from "react"; interface AgentEntry { label: string; desc: string; - cmd: string; + cmd?: string; cookbookId?: string; before: string; after: string; @@ -90,7 +90,6 @@ const codeFrameworks: AgentEntry[] = [ { label: "LangGraph", desc: "Translates StateGraph nodes, edges, and tools into OpenGAP agents and tool YAMLs.", - cmd: "$ opengap import -f langgraph", cookbookId: "cookbook-langgraph", before: `langgraph-project/ ├── graph.py @@ -114,7 +113,6 @@ const codeFrameworks: AgentEntry[] = [ { label: "CrewAI", desc: "Maps Crew, Agent roles/goals, and Task definitions to agents/ and workflows/.", - cmd: "$ opengap import -f crewai", cookbookId: "cookbook-crewai", before: `crewai-project/ ├── crew.py @@ -140,7 +138,6 @@ const codeFrameworks: AgentEntry[] = [ { label: "AutoGen", desc: "Converts ConversableAgent teams, tools, and group chats into OpenGAP agents.", - cmd: "$ opengap import -f autogen", cookbookId: "cookbook-autogen", before: `autogen-project/ ├── team.py @@ -165,7 +162,6 @@ const codeFrameworks: AgentEntry[] = [ { label: "OpenAI Agents SDK", desc: "Maps Agent, handoffs, and @function_tool to agent.yaml, agents/, and tools/.", - cmd: "$ opengap import -f openai-agents", cookbookId: "cookbook-openai-agents", before: `openai-agents-project/ ├── main.py @@ -185,7 +181,6 @@ const codeFrameworks: AgentEntry[] = [ { label: "Claude SDK", desc: "Converts Anthropic SDK agents — tools[], SYSTEM_PROMPT, and multi-turn loops into OpenGAP format.", - cmd: "$ opengap import -f claude-sdk", cookbookId: "cookbook-claude-sdk", before: `anthropic-project/ ├── agent.py @@ -204,7 +199,6 @@ const codeFrameworks: AgentEntry[] = [ { label: "Google ADK", desc: "Converts Agent, sub_agents, and AgentTool hierarchies into nested OpenGAP dirs.", - cmd: "$ opengap import -f google-adk", cookbookId: "cookbook-google-adk", before: `adk-project/ ├── agent.py @@ -261,8 +255,14 @@ function AgentCard({ entry, index }: { entry: AgentEntry; index: number }) {
- {entry.cmd} - + {entry.cmd ? ( + <> + {entry.cmd} + + + ) : ( + Manual mapping + )}
@@ -294,9 +294,11 @@ function AgentCard({ entry, index }: { entry: AgentEntry; index: number }) { )} -
- {entry.cmd} -
+ {entry.cmd && ( +
+ {entry.cmd} +
+ )} )} diff --git a/src/components/opengap/OpenGAPNavbar.tsx b/src/components/opengap/OpenGAPNavbar.tsx index c1a9c64..9697987 100644 --- a/src/components/opengap/OpenGAPNavbar.tsx +++ b/src/components/opengap/OpenGAPNavbar.tsx @@ -4,7 +4,12 @@ import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"; import { opengapSidebarGroups } from "@/components/opengap/OpenGAPSidebar"; const allItems = opengapSidebarGroups.flatMap((g) => - g.items.map((item) => ({ ...item, group: g.label, slug: g.slug })) + g.items.flatMap((item) => [ + { id: item.id, label: item.label, group: g.label, slug: g.slug }, + ...(item.childGroups ?? []).flatMap((cg) => + cg.items.map((c) => ({ id: c.id, label: c.label, group: cg.label, slug: g.slug })) + ), + ]) ); interface OpenGAPNavbarProps { @@ -146,6 +151,30 @@ export function OpenGAPNavbar({ variant = "docs" }: OpenGAPNavbarProps) { > {s.label} + {s.childGroups && ( + + )} ))} diff --git a/src/components/opengap/OpenGAPSidebar.tsx b/src/components/opengap/OpenGAPSidebar.tsx index 1ec6844..94706b6 100644 --- a/src/components/opengap/OpenGAPSidebar.tsx +++ b/src/components/opengap/OpenGAPSidebar.tsx @@ -1,4 +1,29 @@ -export const opengapSidebarGroups = [ +import { useEffect, useState } from "react"; +import { ChevronRight } from "lucide-react"; + +export interface SidebarSubItem { + id: string; + label: string; +} + +export interface SidebarSubGroup { + label: string; + items: SidebarSubItem[]; +} + +export interface SidebarItem { + id: string; + label: string; + childGroups?: SidebarSubGroup[]; +} + +export interface SidebarGroup { + label: string; + slug: string; + items: SidebarItem[]; +} + +export const opengapSidebarGroups: SidebarGroup[] = [ { label: "Getting Started", slug: "getting-started", @@ -20,7 +45,33 @@ export const opengapSidebarGroups = [ slug: "features", items: [ { id: "cli", label: "CLI" }, - { id: "import", label: "Import" }, + { + id: "import", + label: "Import", + childGroups: [ + { + label: "File System Agents", + items: [ + { id: "cookbook-claude-code", label: "Claude Code" }, + { id: "cookbook-cursor", label: "Cursor" }, + { id: "cookbook-gemini-cli", label: "Gemini CLI" }, + { id: "cookbook-codex", label: "Codex" }, + { id: "cookbook-opencode", label: "OpenCode" }, + ], + }, + { + label: "Code-Based Frameworks (Manual)", + items: [ + { id: "cookbook-langgraph", label: "LangGraph" }, + { id: "cookbook-crewai", label: "CrewAI" }, + { id: "cookbook-autogen", label: "AutoGen" }, + { id: "cookbook-openai-agents", label: "OpenAI Agents SDK" }, + { id: "cookbook-claude-sdk", label: "Claude SDK" }, + { id: "cookbook-google-adk", label: "Google ADK" }, + ], + }, + ], + }, { id: "export", label: "Export" }, { id: "adapters", label: "Adapters" }, ], @@ -43,6 +94,87 @@ export const opengapSidebarGroups = [ }, ]; +// Flattened, in display order — parents followed by their children. +// Used for prev/next navigation, active-label lookup, and search. +export const opengapAllItems: SidebarSubItem[] = opengapSidebarGroups.flatMap((g) => + g.items.flatMap((item) => + item.childGroups + ? [{ id: item.id, label: item.label }, ...item.childGroups.flatMap((cg) => cg.items)] + : [{ id: item.id, label: item.label }] + ) +); + +function SidebarLeaf({ item, activeSection }: { item: SidebarSubItem; activeSection: string }) { + const isActive = activeSection === item.id; + return ( +
  • + + {item.label} + +
  • + ); +} + +function SidebarParent({ item, activeSection }: { item: SidebarItem; activeSection: string }) { + const childIds = item.childGroups?.flatMap((cg) => cg.items.map((c) => c.id)) ?? []; + const inSubtree = activeSection === item.id || childIds.includes(activeSection); + const isActive = activeSection === item.id; + const [expanded, setExpanded] = useState(inSubtree); + + // Auto-expand whenever the user navigates into this subtree. + useEffect(() => { + if (inSubtree) setExpanded(true); + }, [inSubtree]); + + return ( +
  • +
    + + {item.label} + + +
    + {expanded && item.childGroups && ( +
    + {item.childGroups.map((cg) => ( +
    +

    + {cg.label} +

    +
      + {cg.items.map((c) => ( + + ))} +
    +
    + ))} +
    + )} +
  • + ); +} + export function OpenGAPSidebar({ activeSection }: { activeSection: string }) { return (