github-agent 0.2.2__tar.gz → 0.2.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {github_agent-0.2.2 → github_agent-0.2.5}/PKG-INFO +4 -4
- {github_agent-0.2.2 → github_agent-0.2.5}/README.md +1 -1
- {github_agent-0.2.2 → github_agent-0.2.5}/github_agent/github_agent.py +21 -24
- {github_agent-0.2.2 → github_agent-0.2.5}/github_agent/utils.py +31 -29
- {github_agent-0.2.2 → github_agent-0.2.5}/github_agent.egg-info/PKG-INFO +4 -4
- github_agent-0.2.5/github_agent.egg-info/requires.txt +6 -0
- {github_agent-0.2.2 → github_agent-0.2.5}/pyproject.toml +3 -3
- github_agent-0.2.2/github_agent.egg-info/requires.txt +0 -6
- {github_agent-0.2.2 → github_agent-0.2.5}/LICENSE +0 -0
- {github_agent-0.2.2 → github_agent-0.2.5}/github_agent/__init__.py +0 -0
- {github_agent-0.2.2 → github_agent-0.2.5}/github_agent.egg-info/SOURCES.txt +0 -0
- {github_agent-0.2.2 → github_agent-0.2.5}/github_agent.egg-info/dependency_links.txt +0 -0
- {github_agent-0.2.2 → github_agent-0.2.5}/github_agent.egg-info/entry_points.txt +0 -0
- {github_agent-0.2.2 → github_agent-0.2.5}/github_agent.egg-info/top_level.txt +0 -0
- {github_agent-0.2.2 → github_agent-0.2.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: github-agent
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: GitHub Agent for MCP
|
|
5
5
|
Author-email: Audel Rouhi <knucklessg1@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -12,8 +12,8 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: >=3.10
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: pydantic-ai-slim[a2a,ag-ui,anthropic,fastmcp,google,huggingface,openai,web]>=1.
|
|
16
|
-
Requires-Dist: pydantic-ai-skills
|
|
15
|
+
Requires-Dist: pydantic-ai-slim[a2a,ag-ui,anthropic,fastmcp,google,groq,huggingface,mistral,openai,web]>=1.58.0
|
|
16
|
+
Requires-Dist: pydantic-ai-skills>=v0.4.0
|
|
17
17
|
Requires-Dist: fastapi>=0.128.0
|
|
18
18
|
Requires-Dist: fastmcp
|
|
19
19
|
Requires-Dist: uvicorn
|
|
@@ -43,7 +43,7 @@ Dynamic: license-file
|
|
|
43
43
|

|
|
44
44
|

|
|
45
45
|
|
|
46
|
-
*Version: 0.2.
|
|
46
|
+
*Version: 0.2.5*
|
|
47
47
|
|
|
48
48
|
## Overview
|
|
49
49
|
|
|
@@ -7,6 +7,7 @@ import os
|
|
|
7
7
|
import argparse
|
|
8
8
|
import logging
|
|
9
9
|
import uvicorn
|
|
10
|
+
import httpx
|
|
10
11
|
from typing import Optional, Any
|
|
11
12
|
from contextlib import asynccontextmanager
|
|
12
13
|
|
|
@@ -38,7 +39,7 @@ from pydantic import ValidationError
|
|
|
38
39
|
from pydantic_ai.ui import SSE_CONTENT_TYPE
|
|
39
40
|
from pydantic_ai.ui.ag_ui import AGUIAdapter
|
|
40
41
|
|
|
41
|
-
__version__ = "0.2.
|
|
42
|
+
__version__ = "0.2.5"
|
|
42
43
|
|
|
43
44
|
logging.basicConfig(
|
|
44
45
|
level=logging.INFO,
|
|
@@ -63,7 +64,6 @@ DEFAULT_SKILLS_DIRECTORY = os.getenv("SKILLS_DIRECTORY", get_skills_path())
|
|
|
63
64
|
DEFAULT_ENABLE_WEB_UI = to_boolean(os.getenv("ENABLE_WEB_UI", "False"))
|
|
64
65
|
DEFAULT_SSL_VERIFY = to_boolean(os.getenv("SSL_VERIFY", "True"))
|
|
65
66
|
|
|
66
|
-
# Model Settings
|
|
67
67
|
DEFAULT_MAX_TOKENS = to_integer(os.getenv("MAX_TOKENS", "16384"))
|
|
68
68
|
DEFAULT_TEMPERATURE = to_float(os.getenv("TEMPERATURE", "0.7"))
|
|
69
69
|
DEFAULT_TOP_P = to_float(os.getenv("TOP_P", "1.0"))
|
|
@@ -83,9 +83,6 @@ AGENT_DESCRIPTION = (
|
|
|
83
83
|
"A multi-agent system for interacting with GitHub via delegated specialists."
|
|
84
84
|
)
|
|
85
85
|
|
|
86
|
-
# -------------------------------------------------------------------------
|
|
87
|
-
# 1. System Prompts
|
|
88
|
-
# -------------------------------------------------------------------------
|
|
89
86
|
|
|
90
87
|
SUPERVISOR_SYSTEM_PROMPT = os.environ.get(
|
|
91
88
|
"SUPERVISOR_SYSTEM_PROMPT",
|
|
@@ -264,16 +261,12 @@ SUPPORT_DOCS_AGENT_PROMPT = os.environ.get(
|
|
|
264
261
|
),
|
|
265
262
|
)
|
|
266
263
|
|
|
267
|
-
# -------------------------------------------------------------------------
|
|
268
|
-
# 2. Agent Creation Logic
|
|
269
|
-
# -------------------------------------------------------------------------
|
|
270
|
-
|
|
271
264
|
|
|
272
265
|
def create_agent(
|
|
273
266
|
provider: str = DEFAULT_PROVIDER,
|
|
274
267
|
model_id: str = DEFAULT_MODEL_ID,
|
|
275
|
-
base_url: Optional[str] =
|
|
276
|
-
api_key: Optional[str] =
|
|
268
|
+
base_url: Optional[str] = DEFAULT_LLM_BASE_URL,
|
|
269
|
+
api_key: Optional[str] = DEFAULT_LLM_API_KEY,
|
|
277
270
|
mcp_url: str = DEFAULT_MCP_URL,
|
|
278
271
|
mcp_config: str = DEFAULT_MCP_CONFIG,
|
|
279
272
|
skills_directory: Optional[str] = DEFAULT_SKILLS_DIRECTORY,
|
|
@@ -306,25 +299,29 @@ def create_agent(
|
|
|
306
299
|
extra_body=DEFAULT_EXTRA_BODY,
|
|
307
300
|
)
|
|
308
301
|
|
|
309
|
-
# Load master toolsets
|
|
310
302
|
agent_toolsets = []
|
|
311
303
|
if mcp_url:
|
|
312
304
|
if "sse" in mcp_url.lower():
|
|
313
|
-
server = MCPServerSSE(
|
|
305
|
+
server = MCPServerSSE(
|
|
306
|
+
mcp_url, http_client=httpx.AsyncClient(verify=ssl_verify)
|
|
307
|
+
)
|
|
314
308
|
else:
|
|
315
|
-
server = MCPServerStreamableHTTP(
|
|
309
|
+
server = MCPServerStreamableHTTP(
|
|
310
|
+
mcp_url, http_client=httpx.AsyncClient(verify=ssl_verify)
|
|
311
|
+
)
|
|
316
312
|
agent_toolsets.append(server)
|
|
317
313
|
logger.info(f"Connected to MCP Server: {mcp_url}")
|
|
318
314
|
elif mcp_config:
|
|
319
315
|
mcp_toolset = load_mcp_servers(mcp_config)
|
|
316
|
+
for server in mcp_toolset:
|
|
317
|
+
if hasattr(server, "http_client"):
|
|
318
|
+
server.http_client = httpx.AsyncClient(verify=ssl_verify)
|
|
320
319
|
agent_toolsets.extend(mcp_toolset)
|
|
321
320
|
logger.info(f"Connected to MCP Config JSON: {mcp_toolset}")
|
|
322
321
|
|
|
323
322
|
if skills_directory and os.path.exists(skills_directory):
|
|
324
323
|
agent_toolsets.append(SkillsToolset(directories=[str(skills_directory)]))
|
|
325
324
|
|
|
326
|
-
# Define Tag -> Prompt map
|
|
327
|
-
# Key is the MCP Tool Tag (or set of tags), Value is (SystemPrompt, AgentName)
|
|
328
325
|
agent_defs = {
|
|
329
326
|
"person": (CONTEXT_AGENT_PROMPT, "GitHub_Context_Agent"),
|
|
330
327
|
"workflow": (ACTIONS_AGENT_PROMPT, "GitHub_Actions_Agent"),
|
|
@@ -383,7 +380,6 @@ def create_agent(
|
|
|
383
380
|
)
|
|
384
381
|
child_agents[tag] = agent
|
|
385
382
|
|
|
386
|
-
# Create Supervisor
|
|
387
383
|
supervisor = Agent(
|
|
388
384
|
name=AGENT_NAME,
|
|
389
385
|
system_prompt=SUPERVISOR_SYSTEM_PROMPT,
|
|
@@ -392,9 +388,6 @@ def create_agent(
|
|
|
392
388
|
deps_type=Any,
|
|
393
389
|
)
|
|
394
390
|
|
|
395
|
-
# Define delegation tools
|
|
396
|
-
# We define these explicitly to give the Supervisor clear, typed tools.
|
|
397
|
-
|
|
398
391
|
@supervisor.tool
|
|
399
392
|
async def assign_task_to_context_agent(ctx: RunContext[Any], task: str) -> str:
|
|
400
393
|
"""Assign a task related to user context and general GitHub status to the Context Agent."""
|
|
@@ -570,8 +563,8 @@ def create_agent(
|
|
|
570
563
|
def create_agent_server(
|
|
571
564
|
provider: str = DEFAULT_PROVIDER,
|
|
572
565
|
model_id: str = DEFAULT_MODEL_ID,
|
|
573
|
-
base_url: Optional[str] =
|
|
574
|
-
api_key: Optional[str] =
|
|
566
|
+
base_url: Optional[str] = DEFAULT_LLM_BASE_URL,
|
|
567
|
+
api_key: Optional[str] = DEFAULT_LLM_API_KEY,
|
|
575
568
|
mcp_url: str = DEFAULT_MCP_URL,
|
|
576
569
|
mcp_config: str = DEFAULT_MCP_CONFIG,
|
|
577
570
|
skills_directory: Optional[str] = DEFAULT_SKILLS_DIRECTORY,
|
|
@@ -582,7 +575,12 @@ def create_agent_server(
|
|
|
582
575
|
ssl_verify: bool = DEFAULT_SSL_VERIFY,
|
|
583
576
|
):
|
|
584
577
|
print(
|
|
585
|
-
f"Starting {AGENT_NAME}
|
|
578
|
+
f"Starting {AGENT_NAME}:"
|
|
579
|
+
f"\tprovider={provider}"
|
|
580
|
+
f"\tmodel={model_id}"
|
|
581
|
+
f"\tbase_url={base_url}"
|
|
582
|
+
f"\tmcp={mcp_url} | {mcp_config}"
|
|
583
|
+
f"\tssl_verify={ssl_verify}"
|
|
586
584
|
)
|
|
587
585
|
agent = create_agent(
|
|
588
586
|
provider=provider,
|
|
@@ -651,7 +649,6 @@ def create_agent_server(
|
|
|
651
649
|
status_code=422,
|
|
652
650
|
)
|
|
653
651
|
|
|
654
|
-
# Prune large messages from history
|
|
655
652
|
if hasattr(run_input, "messages"):
|
|
656
653
|
run_input.messages = prune_large_messages(run_input.messages)
|
|
657
654
|
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
import os
|
|
5
5
|
|
|
6
|
-
# Try importing specific clients/providers, but don't fail if variables are missing
|
|
7
6
|
try:
|
|
8
7
|
|
|
9
8
|
from openai import AsyncOpenAI
|
|
@@ -128,22 +127,17 @@ def prune_large_messages(messages: list[Any], max_length: int = 5000) -> list[An
|
|
|
128
127
|
f"... {content[-200:]}"
|
|
129
128
|
)
|
|
130
129
|
|
|
131
|
-
# Replace content
|
|
132
130
|
if isinstance(msg, dict):
|
|
133
131
|
msg["content"] = summary
|
|
134
132
|
pruned_messages.append(msg)
|
|
135
133
|
elif hasattr(msg, "content"):
|
|
136
|
-
# Try to create a copy or modify in place if mutable
|
|
137
|
-
# If it's a Pydantic model it might be immutable or require copy
|
|
138
134
|
try:
|
|
139
|
-
# Attempt shallow copy with update
|
|
140
135
|
from copy import copy
|
|
141
136
|
|
|
142
137
|
new_msg = copy(msg)
|
|
143
138
|
new_msg.content = summary
|
|
144
139
|
pruned_messages.append(new_msg)
|
|
145
140
|
except Exception:
|
|
146
|
-
# Fallback: keep original if we can't modify
|
|
147
141
|
pruned_messages.append(msg)
|
|
148
142
|
else:
|
|
149
143
|
pruned_messages.append(msg)
|
|
@@ -218,7 +212,6 @@ def load_skills_from_directory(directory: str) -> List[Skill]:
|
|
|
218
212
|
if skill_file.exists():
|
|
219
213
|
try:
|
|
220
214
|
with open(skill_file, "r") as f:
|
|
221
|
-
# Extract frontmatter
|
|
222
215
|
content = f.read()
|
|
223
216
|
if content.startswith("---"):
|
|
224
217
|
_, frontmatter, _ = content.split("---", 2)
|
|
@@ -254,35 +247,47 @@ def get_http_client(ssl_verify: bool = True) -> httpx.AsyncClient | None:
|
|
|
254
247
|
def create_model(
|
|
255
248
|
provider: str,
|
|
256
249
|
model_id: str,
|
|
257
|
-
base_url: Optional[str]
|
|
258
|
-
api_key: Optional[str]
|
|
250
|
+
base_url: Optional[str],
|
|
251
|
+
api_key: Optional[str],
|
|
259
252
|
ssl_verify: bool = True,
|
|
260
253
|
):
|
|
261
|
-
|
|
254
|
+
"""
|
|
255
|
+
Create a Pydantic AI model with the specified provider and configuration.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
provider: The model provider (openai, anthropic, google, groq, mistral, huggingface, ollama)
|
|
259
|
+
model_id: The specific model ID to use
|
|
260
|
+
base_url: Optional base URL for the API
|
|
261
|
+
api_key: Optional API key
|
|
262
|
+
ssl_verify: Whether to verify SSL certificates (default: True)
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
A Pydantic AI Model instance
|
|
266
|
+
"""
|
|
267
|
+
http_client = None
|
|
268
|
+
if not ssl_verify:
|
|
269
|
+
http_client = httpx.AsyncClient(verify=False)
|
|
262
270
|
|
|
263
271
|
if provider == "openai":
|
|
264
272
|
target_base_url = base_url
|
|
265
273
|
target_api_key = api_key
|
|
266
274
|
|
|
267
|
-
# If we have a custom client or specific settings, we might want to use the explicit provider object
|
|
268
275
|
if http_client and AsyncOpenAI and OpenAIProvider:
|
|
269
276
|
client = AsyncOpenAI(
|
|
270
|
-
api_key=target_api_key or os.environ.get("
|
|
271
|
-
base_url=target_base_url or os.environ.get("
|
|
277
|
+
api_key=target_api_key or os.environ.get("OPENAI_API_KEY"),
|
|
278
|
+
base_url=target_base_url or os.environ.get("OPENAI_BASE_URL"),
|
|
272
279
|
http_client=http_client,
|
|
273
280
|
)
|
|
274
281
|
provider_instance = OpenAIProvider(openai_client=client)
|
|
275
282
|
return OpenAIChatModel(model_name=model_id, provider=provider_instance)
|
|
276
283
|
|
|
277
|
-
# Fallback to standard env vars
|
|
278
284
|
if target_base_url:
|
|
279
|
-
os.environ["
|
|
285
|
+
os.environ["OPENAI_BASE_URL"] = target_base_url
|
|
280
286
|
if target_api_key:
|
|
281
|
-
os.environ["
|
|
287
|
+
os.environ["OPENAI_API_KEY"] = target_api_key
|
|
282
288
|
return OpenAIChatModel(model_name=model_id, provider="openai")
|
|
283
289
|
|
|
284
290
|
elif provider == "ollama":
|
|
285
|
-
# Ollama is OpenAI compatible
|
|
286
291
|
target_base_url = base_url or "http://localhost:11434/v1"
|
|
287
292
|
target_api_key = api_key or "ollama"
|
|
288
293
|
|
|
@@ -295,18 +300,18 @@ def create_model(
|
|
|
295
300
|
provider_instance = OpenAIProvider(openai_client=client)
|
|
296
301
|
return OpenAIChatModel(model_name=model_id, provider=provider_instance)
|
|
297
302
|
|
|
298
|
-
os.environ["
|
|
299
|
-
os.environ["
|
|
303
|
+
os.environ["OPENAI_BASE_URL"] = target_base_url
|
|
304
|
+
os.environ["OPENAI_API_KEY"] = target_api_key
|
|
300
305
|
return OpenAIChatModel(model_name=model_id, provider="openai")
|
|
301
306
|
|
|
302
307
|
elif provider == "anthropic":
|
|
303
308
|
if api_key:
|
|
304
|
-
os.environ["
|
|
309
|
+
os.environ["ANTHROPIC_API_KEY"] = api_key
|
|
305
310
|
|
|
306
311
|
try:
|
|
307
312
|
if http_client and AsyncAnthropic and AnthropicProvider:
|
|
308
313
|
client = AsyncAnthropic(
|
|
309
|
-
api_key=api_key or os.environ.get("
|
|
314
|
+
api_key=api_key or os.environ.get("ANTHROPIC_API_KEY"),
|
|
310
315
|
http_client=http_client,
|
|
311
316
|
)
|
|
312
317
|
provider_instance = AnthropicProvider(anthropic_client=client)
|
|
@@ -317,18 +322,17 @@ def create_model(
|
|
|
317
322
|
return AnthropicModel(model_name=model_id)
|
|
318
323
|
|
|
319
324
|
elif provider == "google":
|
|
320
|
-
# Google generic setup, skipping complex SSL for now as agreed
|
|
321
325
|
if api_key:
|
|
322
|
-
os.environ["
|
|
326
|
+
os.environ["GEMINI_API_KEY"] = api_key
|
|
323
327
|
return GoogleModel(model_name=model_id)
|
|
324
328
|
|
|
325
329
|
elif provider == "groq":
|
|
326
330
|
if api_key:
|
|
327
|
-
os.environ["
|
|
331
|
+
os.environ["GROQ_API_KEY"] = api_key
|
|
328
332
|
|
|
329
333
|
if http_client and AsyncGroq and GroqProvider:
|
|
330
334
|
client = AsyncGroq(
|
|
331
|
-
api_key=api_key or os.environ.get("
|
|
335
|
+
api_key=api_key or os.environ.get("GROQ_API_KEY"),
|
|
332
336
|
http_client=http_client,
|
|
333
337
|
)
|
|
334
338
|
provider_instance = GroqProvider(groq_client=client)
|
|
@@ -338,18 +342,16 @@ def create_model(
|
|
|
338
342
|
|
|
339
343
|
elif provider == "mistral":
|
|
340
344
|
if api_key:
|
|
341
|
-
os.environ["
|
|
345
|
+
os.environ["MISTRAL_API_KEY"] = api_key
|
|
342
346
|
|
|
343
347
|
if http_client and Mistral and MistralProvider:
|
|
344
|
-
# Assuming mistral_client argument for MistralProvider
|
|
345
|
-
# Ideally we would verify this, but we'll try standard pattern
|
|
346
348
|
pass
|
|
347
349
|
|
|
348
350
|
return MistralModel(model_name=model_id)
|
|
349
351
|
|
|
350
352
|
elif provider == "huggingface":
|
|
351
353
|
if api_key:
|
|
352
|
-
os.environ["
|
|
354
|
+
os.environ["HUGGING_FACE_API_KEY"] = api_key
|
|
353
355
|
return HuggingFaceModel(model_name=model_id)
|
|
354
356
|
|
|
355
357
|
return OpenAIChatModel(model_name=model_id, provider="openai")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: github-agent
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: GitHub Agent for MCP
|
|
5
5
|
Author-email: Audel Rouhi <knucklessg1@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -12,8 +12,8 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: >=3.10
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: pydantic-ai-slim[a2a,ag-ui,anthropic,fastmcp,google,huggingface,openai,web]>=1.
|
|
16
|
-
Requires-Dist: pydantic-ai-skills
|
|
15
|
+
Requires-Dist: pydantic-ai-slim[a2a,ag-ui,anthropic,fastmcp,google,groq,huggingface,mistral,openai,web]>=1.58.0
|
|
16
|
+
Requires-Dist: pydantic-ai-skills>=v0.4.0
|
|
17
17
|
Requires-Dist: fastapi>=0.128.0
|
|
18
18
|
Requires-Dist: fastmcp
|
|
19
19
|
Requires-Dist: uvicorn
|
|
@@ -43,7 +43,7 @@ Dynamic: license-file
|
|
|
43
43
|

|
|
44
44
|

|
|
45
45
|
|
|
46
|
-
*Version: 0.2.
|
|
46
|
+
*Version: 0.2.5*
|
|
47
47
|
|
|
48
48
|
## Overview
|
|
49
49
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "github-agent"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.5"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
description = "GitHub Agent for MCP"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -18,8 +18,8 @@ classifiers = [
|
|
|
18
18
|
"Programming Language :: Python :: 3",
|
|
19
19
|
]
|
|
20
20
|
dependencies = [
|
|
21
|
-
"pydantic-ai-slim[fastmcp,openai,anthropic,google,huggingface,a2a,ag-ui,web]>=1.
|
|
22
|
-
"pydantic-ai-skills",
|
|
21
|
+
"pydantic-ai-slim[fastmcp,openai,anthropic,groq,mistral,google,huggingface,a2a,ag-ui,web]>=1.58.0",
|
|
22
|
+
"pydantic-ai-skills>=v0.4.0",
|
|
23
23
|
"fastapi>=0.128.0",
|
|
24
24
|
"fastmcp",
|
|
25
25
|
"uvicorn",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|