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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github-agent
3
- Version: 0.2.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.32.0
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
  ![PyPI - Wheel](https://img.shields.io/pypi/wheel/github-agent)
44
44
  ![PyPI - Implementation](https://img.shields.io/pypi/implementation/github-agent)
45
45
 
46
- *Version: 0.2.2*
46
+ *Version: 0.2.5*
47
47
 
48
48
  ## Overview
49
49
 
@@ -21,7 +21,7 @@
21
21
  ![PyPI - Wheel](https://img.shields.io/pypi/wheel/github-agent)
22
22
  ![PyPI - Implementation](https://img.shields.io/pypi/implementation/github-agent)
23
23
 
24
- *Version: 0.2.2*
24
+ *Version: 0.2.5*
25
25
 
26
26
  ## Overview
27
27
 
@@ -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.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] = None,
276
- api_key: Optional[str] = None,
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(mcp_url)
305
+ server = MCPServerSSE(
306
+ mcp_url, http_client=httpx.AsyncClient(verify=ssl_verify)
307
+ )
314
308
  else:
315
- server = MCPServerStreamableHTTP(mcp_url)
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] = None,
574
- api_key: Optional[str] = None,
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} with provider={provider}, model={model_id}, mcp={mcp_url} | {mcp_config}"
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] = None,
258
- api_key: Optional[str] = None,
250
+ base_url: Optional[str],
251
+ api_key: Optional[str],
259
252
  ssl_verify: bool = True,
260
253
  ):
261
- http_client = get_http_client(ssl_verify=ssl_verify)
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("LLM_API_KEY"),
271
- base_url=target_base_url or os.environ.get("LLM_BASE_URL"),
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["LLM_BASE_URL"] = target_base_url
285
+ os.environ["OPENAI_BASE_URL"] = target_base_url
280
286
  if target_api_key:
281
- os.environ["LLM_API_KEY"] = target_api_key
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["LLM_BASE_URL"] = target_base_url
299
- os.environ["LLM_API_KEY"] = target_api_key
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["LLM_API_KEY"] = api_key
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("LLM_API_KEY"),
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["LLM_API_KEY"] = api_key
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["LLM_API_KEY"] = api_key
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("LLM_API_KEY"),
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["LLM_API_KEY"] = api_key
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["LLM_API_KEY"] = api_key
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.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.32.0
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
  ![PyPI - Wheel](https://img.shields.io/pypi/wheel/github-agent)
44
44
  ![PyPI - Implementation](https://img.shields.io/pypi/implementation/github-agent)
45
45
 
46
- *Version: 0.2.2*
46
+ *Version: 0.2.5*
47
47
 
48
48
  ## Overview
49
49
 
@@ -0,0 +1,6 @@
1
+ pydantic-ai-slim[a2a,ag-ui,anthropic,fastmcp,google,groq,huggingface,mistral,openai,web]>=1.58.0
2
+ pydantic-ai-skills>=v0.4.0
3
+ fastapi>=0.128.0
4
+ fastmcp
5
+ uvicorn
6
+ fastapi
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "github-agent"
7
- version = "0.2.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.32.0",
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",
@@ -1,6 +0,0 @@
1
- pydantic-ai-slim[a2a,ag-ui,anthropic,fastmcp,google,huggingface,openai,web]>=1.32.0
2
- pydantic-ai-skills
3
- fastapi>=0.128.0
4
- fastmcp
5
- uvicorn
6
- fastapi
File without changes
File without changes