OmniRoute A2A Server (Türkçe)
🌐 Languages: 🇺🇸 English · 🇪🇸 es · 🇫🇷 fr · 🇩🇪 de · 🇮🇹 it · 🇷🇺 ru · 🇨🇳 zh-CN · 🇯🇵 ja · 🇰🇷 ko · 🇸🇦 ar · 🇮🇳 hi · 🇮🇳 in · 🇹🇭 th · 🇻🇳 vi · 🇮🇩 id · 🇲🇾 ms · 🇳🇱 nl · 🇵🇱 pl · 🇸🇪 sv · 🇳🇴 no · 🇩🇰 da · 🇫🇮 fi · 🇵🇹 pt · 🇷🇴 ro · 🇭🇺 hu · 🇧🇬 bg · 🇸🇰 sk · 🇺🇦 uk-UA · 🇮🇱 he · 🇵🇭 phi · 🇧🇷 pt-BR · 🇨🇿 cs · 🇹🇷 tr
Ajan-Ajan Protokolü v0.3— Herhangi bir AI aracısının, JSON-RPC 2.0 aracılığıyla OmniRoute'u akıllı bir yönlendirme aracısı olarak kullanmasına olanak tanır.
A2A Sunucusu, OmniRoute'u diğer aracıların keşfedebileceği, görevleri devredebileceği ve A2A Protokolünü kullanarak işbirliği yapabileceğibirinci sınıf bir aracıolarak kullanıma sunar.---
Mimari
┌──────────────────────────────────────────────────────────────────┐
│ Orchestrator Agent │
│ (LangChain, CrewAI, AutoGen, Custom Agent) │
└──────────────────────┬───────────────────────────────────────────┘
│ 1. GET /.well-known/agent.json (discover)
│ 2. POST /a2a (JSON-RPC 2.0)
▼
┌──────────────────────────────────────────────────────────────────┐
│ OmniRoute A2A Server │
│ ┌────────────────┐ ┌────────────────┐ ┌───────────────────┐ │
│ │ Task Manager │ │ Skill Engine │ │ SSE Streaming │ │
│ │ (lifecycle) │──│ (registry) │──│ (real-time) │ │
│ └────────────────┘ └────────┬───────┘ └───────────────────┘ │
│ │ │
│ Skills: │ │
│ ├─ smart-routing ──────────┤ ┌────────────────────────────┐ │
│ └─ quota-management ───────┘ │ Routing Decision Logger │ │
│ └────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
│
▼ OmniRoute Gateway (internal)
/v1/chat/completions, /api/combos, /api/usage/quota
Hızlı Başlangıç
Agent Discovery
Her A2A uyumlu aracı, /.well-known/agent.json adresinde birAjan Kartıgösterir:```bash
curl http://localhost:20128/.well-known/agent.json
**Cevap:**```json
{
"name": "OmniRoute",
"description": "Intelligent AI gateway with auto-routing across 50+ providers",
"url": "http://localhost:20128/a2a",
"version": "1.8.1",
"capabilities": {
"streaming": true,
"pushNotifications": false
},
"skills": [
{
"id": "smart-routing",
"name": "Smart Routing",
"description": "Routes prompts through OmniRoute intelligent pipeline",
"tags": ["routing", "llm", "multi-provider", "cost-optimization"],
"examples": [
"Write a hello world in Python",
"Explain quantum computing using the cheapest provider"
]
},
{
"id": "quota-management",
"name": "Quota Management",
"description": "Natural-language queries about provider quotas",
"tags": ["quota", "analytics", "cost"],
"examples": [
"Which provider has the most quota remaining?",
"Suggest a free combo for coding"
]
}
],
"authentication": {
"schemes": ["bearer"],
"apiKeyHeader": "Authorization"
}
}
JSON-RPC 2.0 Methods
message/send — Synchronous Execution
Bir beceriye mesaj gönderin ve tam yanıtı alın.```bash
curl -X POST http://localhost:20128/a2a
-H "Content-Type: application/json"
-H "Authorization: Bearer YOUR_KEY"
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "message/send",
"params": {
"skill": "smart-routing",
"messages": [{"role": "user", "content": "Write a Python hello world"}],
"metadata": {"model": "auto", "combo": "fast-coding"}
}
}'
**Cevap:**```json
{
"jsonrpc": "2.0",
"id": "1",
"result": {
"task": { "id": "a1b2c3d4-...", "state": "completed" },
"artifacts": [{ "type": "text", "content": "print('Hello, World!')" }],
"metadata": {
"routing_explanation": "Selected claude-sonnet via provider \"anthropic\" (latency: 1200ms, cost: $0.0030)",
"cost_envelope": { "estimated": 0.005, "actual": 0.003, "currency": "USD" },
"resilience_trace": [
{ "event": "primary_selected", "provider": "anthropic", "timestamp": "2026-03-04T..." }
],
"policy_verdict": { "allowed": true, "reason": "within budget and quota limits" }
}
}
}
message/stream — SSE Streaming
"Mesaj/gönder" ile aynıdır ancak gerçek zamanlı akış için Sunucudan Gönderilen Olayları döndürür.```bash
curl -N -X POST http://localhost:20128/a2a
-H "Content-Type: application/json"
-H "Authorization: Bearer YOUR_KEY"
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "message/stream",
"params": {
"skill": "smart-routing",
"messages": [{"role": "user", "content": "Explain quantum computing"}]
}
}'
**SSE Etkinlikleri:**```
data: {"jsonrpc":"2.0","method":"message/stream","params":{"task":{"id":"...","state":"working"},"chunk":{"type":"text","content":"Quantum computing..."}}}
: heartbeat 2026-03-04T21:00:00Z
data: {"jsonrpc":"2.0","method":"message/stream","params":{"task":{"id":"...","state":"completed"},"metadata":{...}}}
tasks/get — Query Task Status
curl -X POST http://localhost:20128/a2a \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"jsonrpc":"2.0","id":"2","method":"tasks/get","params":{"taskId":"TASK_UUID"}}'
tasks/cancel — Cancel a Running Task
curl -X POST http://localhost:20128/a2a \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_KEY" \
-d '{"jsonrpc":"2.0","id":"3","method":"tasks/cancel","params":{"taskId":"TASK_UUID"}}'
Skills Reference
smart-routing
Rotalar, OmniRoute'un akıllı işlem hattı üzerinden tam gözlemlenebilirliğe sahip yönlendirmeler yapar.
Parametreler ("meta verilerde"):
| Parametre | Tür | Varsayılan | Açıklama |
|---|---|---|---|
| 'modeli' | 'dize' | "otomatik" |
Hedef modeli (ör. "claude-sonnet-4", "gpt-4o", "auto") |
| 'kombinasyon' | 'dize' | aktif kombo | Yönlendirmek için özel kombinasyon |
| 'bütçe' | 'sayı' | hiçbiri | Bu isteğin USD cinsinden maksimum maliyeti |
| 'rol' | 'dize' | hiçbiri | Görev rolü ipucu: kodlama, gözden geçirme, planlama, analiz, hata ayıklama, belgeleme |
İade:
| Alan | Açıklama | |
|---|---|---|
yapılar[].içerik |
LLM yanıt metni | |
metadata.routing_explanation |
Yönlendirme kararının insan tarafından okunabilen açıklaması | |
metadata.cost_envelope |
Para birimiyle tahmini ve gerçek maliyet | |
| 'metadata.resilience_trace' | Olay dizisi (primary_selected, fallback_needed, vb.) | |
metadata.policy_verdict |
İsteğe izin verilip verilmediği ve nedeni | ### quota-management |
Sağlayıcı kotalarıyla ilgili doğal dildeki sorguları yanıtlar.
Sorgu türleri (mesaj içeriğinden çıkarılmıştır):
| Sorgu Modeli | Yanıt Türü | |
|---|---|---|
"sıralama", en fazla kota, en iyi içerir |
Sağlayıcılar kalan kotaya göre sıralandı | |
| "Ücretsiz", "öneri" içerir | Ücretsiz kombinasyonları listeler veya ücretsiz katmanlı sağlayıcılar önerir | |
| Varsayılan | Düşük kotaya sahip sağlayıcılar için uyarılarla birlikte tam kota özeti | --- |
Task Lifecycle
submitted ──→ working ──→ completed
──→ failed
──────────→ cancelled
| Devlet | Açıklama |
|---|---|
| 'gönderildi' | Görev oluşturuldu, yürütülmek üzere sıraya alındı |
| 'çalışıyor' | Beceri işleyicisi yürütülüyor |
| 'tamamlandı' | Yürütme başarılı oldu, yapılar mevcut |
| 'başarısız' | Yürütme başarısız oldu veya görevin süresi doldu (TTL: varsayılan 5 dakika) |
| 'iptal edildi' | İstemci tarafından 'görevler/iptal' yoluyla iptal edildi |
- Terminal durumları: "tamamlandı", "başarısız oldu", "iptal edildi" (başka geçiş yok)
- "Gönderildi" veya "çalışıyor" durumundaki süresi dolmuş görevler otomatik olarak "başarısız" olarak işaretlenir
- Görevler 2× TTL'den sonra çöp olarak toplanır---
Client Examples
Python — Orchestrator Agent
"""
A2A Client — Python example.
Discovers OmniRoute agent, sends a task, and processes the result.
"""
import requests
import json
BASE_URL = "http://localhost:20128"
API_KEY = "your-api-key"
HEADERS = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}",
}
# 1. Discover agent capabilities
agent_card = requests.get(f"{BASE_URL}/.well-known/agent.json").json()
print(f"Agent: {agent_card['name']} v{agent_card['version']}")
print(f"Skills: {[s['id'] for s in agent_card['skills']]}")
# 2. Send a smart-routing task
response = requests.post(f"{BASE_URL}/a2a", headers=HEADERS, json={
"jsonrpc": "2.0",
"id": "task-1",
"method": "message/send",
"params": {
"skill": "smart-routing",
"messages": [{"role": "user", "content": "Write a Python quicksort implementation"}],
"metadata": {
"model": "auto",
"combo": "fast-coding",
"budget": 0.10,
}
}
})
result = response.json()["result"]
print(f"\n📝 Response: {result['artifacts'][0]['content'][:200]}...")
print(f"🔀 Routing: {result['metadata']['routing_explanation']}")
print(f"💰 Cost: ${result['metadata']['cost_envelope']['actual']}")
print(f"🛡️ Policy: {result['metadata']['policy_verdict']['reason']}")
# 3. Query quota status
quota_resp = requests.post(f"{BASE_URL}/a2a", headers=HEADERS, json={
"jsonrpc": "2.0",
"id": "task-2",
"method": "message/send",
"params": {
"skill": "quota-management",
"messages": [{"role": "user", "content": "Which provider has the most quota remaining?"}],
}
})
quota_result = quota_resp.json()["result"]
print(f"\n📊 Quota: {quota_result['artifacts'][0]['content']}")
TypeScript — Multi-Agent Orchestrator
/**
* A2A Client — TypeScript example.
* Shows agent discovery, task delegation, and streaming.
*/
const BASE_URL = "http://localhost:20128";
const API_KEY = "your-api-key";
interface JsonRpcResponse<T = any> {
jsonrpc: "2.0";
id: string | number;
result?: T;
error?: { code: number; message: string };
}
async function a2aCall<T>(method: string, params: Record<string, any>): Promise<T> {
const resp = await fetch(`${BASE_URL}/a2a`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${API_KEY}`,
},
body: JSON.stringify({
jsonrpc: "2.0",
id: `${method}-${Date.now()}`,
method,
params,
}),
});
const json: JsonRpcResponse<T> = await resp.json();
if (json.error) throw new Error(`[${json.error.code}] ${json.error.message}`);
return json.result!;
}
// ── Agent Discovery ──
const agentCard = await fetch(`${BASE_URL}/.well-known/agent.json`).then((r) => r.json());
console.log(`Connected to: ${agentCard.name} (${agentCard.skills.length} skills)`);
// ── Smart Routing: Send a coding task ──
const routingResult = await a2aCall("message/send", {
skill: "smart-routing",
messages: [{ role: "user", content: "Implement a Redis cache wrapper in TypeScript" }],
metadata: { model: "claude-sonnet-4", role: "coding" },
});
console.log("Response:", routingResult.artifacts[0].content);
console.log("Provider:", routingResult.metadata.routing_explanation);
// ── Quota Management: Find free alternatives ──
const quotaResult = await a2aCall("message/send", {
skill: "quota-management",
messages: [{ role: "user", content: "Suggest free combos for documentation" }],
});
console.log("Free combos:", quotaResult.artifacts[0].content);
// ── Streaming: Real-time response ──
const streamResp = await fetch(`${BASE_URL}/a2a`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${API_KEY}`,
},
body: JSON.stringify({
jsonrpc: "2.0",
id: "stream-1",
method: "message/stream",
params: {
skill: "smart-routing",
messages: [{ role: "user", content: "Explain microservices architecture" }],
},
}),
});
const reader = streamResp.body!.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
for (const line of chunk.split("\n")) {
if (line.startsWith("data: ")) {
const event = JSON.parse(line.slice(6));
if (event.params.chunk) {
process.stdout.write(event.params.chunk.content);
}
if (event.params.task.state === "completed") {
console.log("\n✅ Stream completed");
}
}
}
}
Python — LangChain A2A Integration
"""
LangChain integration — Use OmniRoute A2A as a custom LLM.
"""
from langchain.llms.base import BaseLLM
from langchain.schema import LLMResult, Generation
import requests
from typing import List, Optional
class OmniRouteA2A(BaseLLM):
base_url: str = "http://localhost:20128"
api_key: str = ""
model: str = "auto"
combo: Optional[str] = None
@property
def _llm_type(self) -> str:
return "omniroute-a2a"
def _call(self, prompt: str, stop: Optional[List[str]] = None, **kwargs) -> str:
response = requests.post(
f"{self.base_url}/a2a",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}",
},
json={
"jsonrpc": "2.0",
"id": "langchain-1",
"method": "message/send",
"params": {
"skill": "smart-routing",
"messages": [{"role": "user", "content": prompt}],
"metadata": {
"model": self.model,
**({"combo": self.combo} if self.combo else {}),
},
},
},
)
result = response.json()["result"]
return result["artifacts"][0]["content"]
def _generate(self, prompts: List[str], stop=None, **kwargs) -> LLMResult:
return LLMResult(
generations=[[Generation(text=self._call(p, stop))] for p in prompts]
)
# Usage
llm = OmniRouteA2A(
base_url="http://localhost:20128",
api_key="your-key",
model="auto",
combo="fast-coding",
)
result = llm("Write a Python function to merge two sorted lists")
print(result)
Go — A2A Client
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
const baseURL = "http://localhost:20128"
const apiKey = "your-api-key"
type JsonRpcRequest struct {
Jsonrpc string `json:"jsonrpc"`
ID string `json:"id"`
Method string `json:"method"`
Params interface{} `json:"params"`
}
type JsonRpcResponse struct {
Jsonrpc string `json:"jsonrpc"`
ID string `json:"id"`
Result interface{} `json:"result"`
Error *struct {
Code int `json:"code"`
Message string `json:"message"`
} `json:"error"`
}
func a2aCall(method string, params interface{}) (*JsonRpcResponse, error) {
body, _ := json.Marshal(JsonRpcRequest{
Jsonrpc: "2.0",
ID: "go-1",
Method: method,
Params: params,
})
req, _ := http.NewRequest("POST", baseURL+"/a2a", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
var result JsonRpcResponse
json.Unmarshal(data, &result)
return &result, nil
}
func main() {
// Discover agent
resp, _ := http.Get(baseURL + "/.well-known/agent.json")
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println("Agent Card:", string(body))
// Send smart-routing task
result, _ := a2aCall("message/send", map[string]interface{}{
"skill": "smart-routing",
"messages": []map[string]string{{"role": "user", "content": "Hello from Go!"}},
"metadata": map[string]interface{}{"model": "auto"},
})
out, _ := json.MarshalIndent(result.Result, "", " ")
fmt.Println("Result:", string(out))
}
Use Cases
🤖 Use Case 1: Multi-Agent Coding Pipeline
Bir orkestratör aracısı, kod oluşturma yetkisini OmniRoute'a devreder, ardından çıktıyı bir inceleme aracısına iletir.```python def coding_pipeline(task: str): # Step 1: Generate code via OmniRoute A2A code_result = a2a_send("smart-routing", [ {"role": "user", "content": f"Write production-quality code: {task}"} ], metadata={"model": "auto", "role": "coding"}) code = code_result["artifacts"][0]["content"]
# Step 2: Review the code via OmniRoute A2A (different model)
review_result = a2a_send("smart-routing", [
{"role": "user", "content": f"Review this code for bugs and improvements:\n\n{code}"}
], metadata={"model": "auto", "role": "review"})
review = review_result["artifacts"][0]["content"]
# Step 3: Check costs
print(f"Code cost: ${code_result['metadata']['cost_envelope']['actual']}")
print(f"Review cost: ${review_result['metadata']['cost_envelope']['actual']}")
return {"code": code, "review": review}
### 💡 Use Case 2: Quota-Aware Agent Swarm
Birden fazla aracı, koordinasyon için kota becerisini kullanarak OmniRoute aracılığıyla kotayı paylaşır.```python
async def quota_aware_agent(agent_name: str, task: str):
# Check quota before starting
quota = a2a_send("quota-management", [
{"role": "user", "content": "Which provider has the most quota remaining?"}
])
print(f"[{agent_name}] {quota['artifacts'][0]['content']}")
# Send request with budget constraint
result = a2a_send("smart-routing", [
{"role": "user", "content": task}
], metadata={"budget": 0.05})
policy = result["metadata"]["policy_verdict"]
if not policy["allowed"]:
print(f"[{agent_name}] ⚠️ Budget exceeded: {policy['reason']}")
# Fall back to free combo
quota = a2a_send("quota-management", [
{"role": "user", "content": "Suggest free combos"}
])
print(f"[{agent_name}] Free alternatives: {quota['artifacts'][0]['content']}")
return result
📊 Use Case 3: Real-Time Streaming Dashboard
Bir izleme aracısı yanıtları aktarır ve ilerlemeyi gerçek zamanlı olarak görüntüler.```typescript
async function streamingDashboard(prompt: string) {
const response = await fetch(${BASE_URL}/a2a, {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: Bearer ${API_KEY} },
body: JSON.stringify({
jsonrpc: "2.0",
id: "dash-1",
method: "message/stream",
params: { skill: "smart-routing", messages: [{ role: "user", content: prompt }] },
}),
});
let totalChunks = 0; const reader = response.body!.getReader(); const decoder = new TextDecoder();
while (true) { const { done, value } = await reader.read(); if (done) break;
for (const line of decoder.decode(value).split("\n")) {
if (line.startsWith("data: ")) {
const event = JSON.parse(line.slice(6));
const state = event.params.task.state;
if (state === "working" && event.params.chunk) {
totalChunks++;
process.stdout.write(
`\r[Chunk ${totalChunks}] ${event.params.chunk.content.slice(0, 50)}...`
);
}
if (state === "completed") {
const meta = event.params.metadata;
console.log(
`\n✅ Done | Cost: $${meta?.cost_envelope?.actual || 0} | Route: ${meta?.routing_explanation || "N/A"}`
);
}
if (state === "failed") {
console.error(`\n❌ Failed: ${event.params.metadata?.error}`);
}
}
}
} }
### 🔁 Use Case 4: Task Polling Pattern
Uzun süren görevler için eşzamanlı olarak beklemek yerine görev durumunu yoklayın.```python
import time
def poll_task(task_id: str, timeout: int = 60):
"""Poll task status until completion or timeout."""
start = time.time()
while time.time() - start < timeout:
result = requests.post(f"{BASE_URL}/a2a", headers=HEADERS, json={
"jsonrpc": "2.0",
"id": "poll-1",
"method": "tasks/get",
"params": {"taskId": task_id},
}).json()
task = result["result"]["task"]
state = task["state"]
print(f" Task {task_id[:8]}... state={state}")
if state in ("completed", "failed", "cancelled"):
return task
time.sleep(2)
# Timeout — cancel the task
requests.post(f"{BASE_URL}/a2a", headers=HEADERS, json={
"jsonrpc": "2.0",
"id": "cancel-1",
"method": "tasks/cancel",
"params": {"taskId": task_id},
})
raise TimeoutError(f"Task {task_id} timed out after {timeout}s")
Error Codes
| Kod | Sabit | Anlamı | |
|---|---|---|---|
| -32700 | — | Ayrıştırma hatası (geçersiz JSON) | |
| -32600 | 'INVALID_REQUEST' | Geçersiz JSON-RPC isteği veya yetkisiz | |
| -32601 | METHOD_NOT_FOUND |
Bilinmeyen yöntem veya beceri | |
| -32602 | 'INVALID_PARAMS' | Eksik veya geçersiz parametreler | |
| -32603 | 'DAHİLİ_HATA' | Becerinin yürütülmesi başarısız oldu | |
| -32001 | TASK_NOT_FOUND |
Görev Kimliği bulunamadı | |
| -32002 | GÖREV_ALREADY_TAMAMLANDI |
Tamamlanmış bir görev değiştirilemiyor | |
| -32003 | 'YETKİSİZ' | Geçersiz veya eksik API anahtarı | |
| -32004 | BUDGET_EXCEEDED |
İstek yapılandırılan bütçeyi aşıyor | |
| -32005 | PROVIDER_UNAVAILABLE |
Kullanılabilir sağlayıcı yok | --- |
Authentication
Tüm /a2a istekleri, Yetkilendirme başlığı aracılığıyla bir Taşıyıcı jetonu gerektirir:```
Authorization: Bearer YOUR_OMNIROUTE_API_KEY
Sunucuda hiçbir API anahtarı yapılandırılmamışsa ("OMNIROUTE_API_KEY` boştur), kimlik doğrulama atlanır.---
## File Structure
src/lib/a2a/ ├── taskManager.ts # Task lifecycle (create/update/cancel/list), TTL, cleanup ├── taskExecution.ts # Generic task executor with state management ├── streaming.ts # SSE stream formatting, heartbeat, chunk/completion events ├── routingLogger.ts # Routing decision logger (stats, history, retention) └── skills/ ├── smartRouting.ts # Smart routing skill (routes via /v1/chat/completions) └── quotaManagement.ts # Quota management skill (natural-language quota queries)
src/app/a2a/ └── route.ts # Next.js API route handler (JSON-RPC 2.0 dispatch)
open-sse/mcp-server/ └── schemas/a2a.ts # Zod schemas (AgentCard, Task, JSON-RPC, SSE events)
---
## Comparison: MCP vs A2A
| Özellik | MCP Sunucusu | A2A Sunucusu |
| ----------------- | ---------------------------- | -------------------------------------------------- |
|**Protokol**| Model Bağlam Protokolü | Temsilci-Acente Protokolü v0.3 |
|**Ulaşım**| stdio / HTTP | HTTP (JSON-RPC 2.0) |
|**Keşif**| MCP aracılığıyla takım listeleme | `/.well-known/agent.json` |
|**Ayrıntılılık**| 16 ayrı alet | 2 üst düzey beceri |
|**Şunlar için en iyisi**| IDE aracıları (İmleç, VS Kodu) | Çoklu ajan sistemleri (LangChain, CrewAI) |
|**Akış**| Desteklenmiyor | 'mesaj/akış' yoluyla SSE |
|**Görev takibi**| Hayır | Tam yaşam döngüsü (gönderildi → tamamlandı) |
|**Gözlemlenebilirlik**| Araç çağrısı başına denetim günlüğü | Maliyet zarfı + esneklik takibi + politika kararı |---
## Lisans
[OmniRoute](https://github.com/diegosouzapw/OmniRoute) — MIT Lisansının bir parçası.