Skip to content

Commit 5b2bf02

Browse files
committed
fix: add runnable Python sample + address review concerns
- Add osop_workflow_reader.py: reads .osop.yaml and creates SK ChatCompletionAgents from agent nodes (addresses design review concern that concepts/ needs runnable Python code) - Remove pip install osop from README (address supply-chain concern) - Update README to describe the Python script usage
1 parent 7795074 commit 5b2bf02

File tree

2 files changed

+130
-11
lines changed

2 files changed

+130
-11
lines changed

python/samples/concepts/osop/README.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,44 @@
22

33
[OSOP](https://github.com/Archie0125/osop-spec) (Open Standard Operating Procedures) is a portable YAML format for describing AI workflows — think OpenAPI, but for agent orchestration.
44

5-
> **Note:** OSOP files are declarative workflow descriptions, not executable code. They document orchestration patterns in a framework-agnostic format.
5+
> **Note:** The OSOP YAML file is a declarative workflow description. The Python script `osop_workflow_reader.py` demonstrates how to read it and create Semantic Kernel agents from the definition.
66
77
## What's Here
88

99
| File | Description |
1010
|------|-------------|
11-
| `code-review-pipeline.osop.yaml` | Automated PR review: fetch diff, parallel analysis (complexity + security + tests), generate review, post comments |
11+
| `code-review-pipeline.osop.yaml` | OSOP workflow definition: automated PR review pipeline |
12+
| `osop_workflow_reader.py` | Python script that reads the OSOP file and creates SK `ChatCompletionAgent` instances |
1213

13-
## Why OSOP?
14+
## How to Run
1415

15-
Semantic Kernel provides powerful AI orchestration with plugins, planners, and agents. OSOP provides a **framework-agnostic way to describe** those orchestration patterns so they can be:
16+
```bash
17+
# Install dependencies
18+
pip install pyyaml semantic-kernel
1619

17-
- **Documented** — readable YAML that non-developers can understand
18-
- **Validated** — check workflow structure before execution
19-
- **Ported** — same workflow definition works across Semantic Kernel, LangChain, AutoGen, etc.
20-
- **Visualized** — render the workflow as a graph in the [OSOP Editor](https://osop-editor.vercel.app)
20+
# Run the sample
21+
cd python/samples/concepts/osop
22+
python osop_workflow_reader.py
23+
```
24+
25+
The script:
26+
1. Loads the `.osop.yaml` workflow definition
27+
2. Prints a human-readable workflow summary
28+
3. Creates `ChatCompletionAgent` instances for each `type: agent` node
29+
4. Invokes the first agent as a demonstration
2130

2231
## How It Maps to Semantic Kernel
2332

2433
| OSOP Concept | Semantic Kernel Equivalent |
2534
|---|---|
26-
| `node` with `type: agent` | `ChatCompletionAgent` / `OpenAIAssistantAgent` |
35+
| `node` with `type: agent` | `ChatCompletionAgent` |
2736
| `node` with `type: api` | Native function / Plugin |
2837
| `edge` with `mode: parallel` | Parallel plugin execution |
2938
| `edge` with `mode: conditional` | `KernelFunction` with conditional logic |
3039
| `config.plugins` | Semantic Kernel plugins |
3140

3241
## Learn More
3342

34-
- [OSOP Spec](https://github.com/Archie0125/osop-spec) — full specification
43+
- [OSOP Spec](https://github.com/Archie0125/osop-spec) — full specification (Apache 2.0)
3544
- [OSOP Examples](https://github.com/Archie0125/osop-examples) — 84+ workflow templates
3645
- [OSOP Visual Editor](https://osop-editor.vercel.app) — visual workflow editor
37-
- [OSOP Website](https://osop.ai) — documentation and guides
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Copyright (c) Microsoft. All rights reserved.
2+
3+
"""
4+
OSOP Workflow Reader for Semantic Kernel
5+
6+
Reads an OSOP (.osop.yaml) workflow definition and creates
7+
Semantic Kernel ChatCompletionAgents from the agent nodes.
8+
Demonstrates how portable OSOP workflow definitions can drive
9+
SK agent orchestration.
10+
11+
Usage:
12+
python osop_workflow_reader.py
13+
"""
14+
15+
import asyncio
16+
from pathlib import Path
17+
18+
import yaml
19+
20+
from semantic_kernel import Kernel
21+
from semantic_kernel.agents import ChatCompletionAgent, ChatHistoryAgentThread
22+
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
23+
24+
# OSOP node type → SK description
25+
NODE_TYPE_DESCRIPTIONS = {
26+
"agent": "AI agent (ChatCompletionAgent)",
27+
"api": "API call (native function)",
28+
"cli": "CLI command execution",
29+
"human": "Human review step",
30+
"system": "System/infrastructure",
31+
"db": "Database operation",
32+
}
33+
34+
35+
def load_osop_workflow(file_path: str) -> dict:
36+
"""Load and parse an OSOP workflow YAML file."""
37+
content = Path(file_path).read_text(encoding="utf-8")
38+
data = yaml.safe_load(content)
39+
if not isinstance(data, dict) or "nodes" not in data:
40+
raise ValueError("Invalid OSOP workflow: missing 'nodes'")
41+
return data
42+
43+
44+
def describe_workflow(workflow: dict) -> str:
45+
"""Generate a human-readable description of an OSOP workflow."""
46+
name = workflow.get("name", workflow.get("id", "Untitled"))
47+
nodes = workflow.get("nodes", [])
48+
edges = workflow.get("edges", [])
49+
lines = [f"Workflow: {name}", f"Nodes: {len(nodes)}, Edges: {len(edges)}", ""]
50+
for node in nodes:
51+
ntype = node.get("type", "system")
52+
nname = node.get("name", node.get("id", "?"))
53+
desc = NODE_TYPE_DESCRIPTIONS.get(ntype, ntype)
54+
lines.append(f" [{ntype}] {nname}{desc}")
55+
return "\n".join(lines)
56+
57+
58+
async def create_agents_from_osop(workflow: dict, kernel: Kernel) -> list[ChatCompletionAgent]:
59+
"""Create SK ChatCompletionAgents from OSOP agent nodes."""
60+
agents = []
61+
for node in workflow.get("nodes", []):
62+
if node.get("type") != "agent":
63+
continue
64+
name = node.get("name", node.get("id", "Agent"))
65+
purpose = node.get("purpose", node.get("description", ""))
66+
config = node.get("config", {})
67+
system_prompt = config.get("system_prompt", f"You are {name}. {purpose}")
68+
69+
agent = ChatCompletionAgent(
70+
kernel=kernel,
71+
name=name.replace(" ", "_"),
72+
instructions=system_prompt,
73+
)
74+
agents.append(agent)
75+
print(f" Created agent: {name}")
76+
return agents
77+
78+
79+
async def main():
80+
# Load the OSOP workflow
81+
osop_path = Path(__file__).parent / "code-review-pipeline.osop.yaml"
82+
if not osop_path.exists():
83+
print(f"OSOP file not found: {osop_path}")
84+
return
85+
86+
workflow = load_osop_workflow(str(osop_path))
87+
print(describe_workflow(workflow))
88+
print()
89+
90+
# Create a kernel with OpenAI
91+
kernel = Kernel()
92+
kernel.add_service(OpenAIChatCompletion(service_id="default"))
93+
94+
# Create agents from OSOP definition
95+
print("Creating agents from OSOP workflow:")
96+
agents = await create_agents_from_osop(workflow, kernel)
97+
print(f"\nCreated {len(agents)} agents from {len(workflow.get('nodes', []))} OSOP nodes")
98+
99+
# Demonstrate: use the first agent
100+
if agents:
101+
thread = ChatHistoryAgentThread()
102+
print(f"\nInvoking agent: {agents[0].name}")
103+
response = await agents[0].get_response(
104+
messages="Describe a simple Python function that calculates factorial.",
105+
thread=thread,
106+
)
107+
print(f"Response: {response.content[:200]}...")
108+
109+
110+
if __name__ == "__main__":
111+
asyncio.run(main())

0 commit comments

Comments
 (0)