ak-agentbase 0.1.0a1__py3-none-any.whl

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.
agentbase/__init__.py ADDED
@@ -0,0 +1,78 @@
1
+ __version__ = "0.1.0a1"
2
+
3
+ from pathlib import Path
4
+
5
+ from agentbase.config import get_environment
6
+ from agentbase.config import load_config as _load_config
7
+ from agentbase.exceptions import (
8
+ AgentBaseError,
9
+ AgentNotRegisteredError,
10
+ ConfigValidationError,
11
+ ContractViolationError,
12
+ ModelResolutionError,
13
+ )
14
+ from agentbase.models import resolve as _resolve
15
+ from agentbase.registry import load_registry
16
+ from agentbase.types import (
17
+ AgentBaseConfig,
18
+ AgentMetadata,
19
+ AgentStatus,
20
+ ResolvedAgent,
21
+ TracingConfig,
22
+ )
23
+
24
+ __all__ = [
25
+ "__version__",
26
+ "load_config",
27
+ "resolve",
28
+ "init_tracing",
29
+ "AgentBaseConfig",
30
+ "AgentMetadata",
31
+ "AgentStatus",
32
+ "ResolvedAgent",
33
+ "TracingConfig",
34
+ "AgentBaseError",
35
+ "AgentNotRegisteredError",
36
+ "ConfigValidationError",
37
+ "ContractViolationError",
38
+ "ModelResolutionError",
39
+ ]
40
+
41
+ _config: AgentBaseConfig | None = None
42
+ _registry: dict[str, AgentMetadata] | None = None
43
+ _environment: str | None = None
44
+
45
+
46
+ def load_config(
47
+ config_path: str | Path | None = None,
48
+ registry_path: str | Path | None = None,
49
+ ) -> AgentBaseConfig:
50
+ global _config, _registry, _environment
51
+
52
+ _config = _load_config(config_path)
53
+ _environment = get_environment(_config)
54
+
55
+ if registry_path is None:
56
+ base = Path(config_path).parent if config_path else Path.cwd()
57
+ registry_path = base / "agents.yaml"
58
+
59
+ _registry = load_registry(registry_path, _config, _environment)
60
+ return _config
61
+
62
+
63
+ def resolve(agent_name: str, environment: str | None = None) -> ResolvedAgent:
64
+ if _config is None or _registry is None:
65
+ raise AgentBaseError(
66
+ "AgentBase is not initialized. Call agentbase.load_config() before using resolve()."
67
+ )
68
+ return _resolve(agent_name, _registry, _config, environment or _environment)
69
+
70
+
71
+ def init_tracing() -> None:
72
+ if _config is None:
73
+ raise AgentBaseError(
74
+ "AgentBase is not initialized. Call agentbase.load_config() before init_tracing()."
75
+ )
76
+ from agentbase.tracing import init_tracing as _init_tracing
77
+
78
+ _init_tracing(_config)
agentbase/__main__.py ADDED
@@ -0,0 +1,3 @@
1
+ from agentbase.cli import main
2
+
3
+ main()
agentbase/cli.py ADDED
@@ -0,0 +1,73 @@
1
+ import argparse
2
+ import os
3
+ import sys
4
+
5
+ from agentbase.config import _config_cache, get_environment, load_config
6
+ from agentbase.exceptions import AgentBaseError
7
+ from agentbase.registry import _registry_cache, load_registry
8
+
9
+
10
+ def _validate(config_path: str, registry_path: str) -> bool:
11
+ """Validate config and registry files.
12
+
13
+ Returns True if valid, False if any errors found.
14
+ """
15
+ _config_cache.clear()
16
+ _registry_cache.clear()
17
+
18
+ config = None
19
+ errors: list[str] = []
20
+
21
+ # Step 1: Validate agentbase.yaml
22
+ try:
23
+ config = load_config(config_path)
24
+ print(f"✓ {config_path} — project: {config.project}")
25
+ except AgentBaseError as e:
26
+ print(f"✗ {e}")
27
+ errors.append(str(e))
28
+
29
+ # Step 2: Validate agents.yaml (if config loaded successfully)
30
+ if config is not None:
31
+ try:
32
+ environment = get_environment(config)
33
+ load_registry(registry_path, config, environment)
34
+ print(f"✓ {registry_path} — environment: {environment}")
35
+ except AgentBaseError as e:
36
+ print(f"✗ {e}")
37
+ errors.append(str(e))
38
+
39
+ # Step 3: Validate tracing env vars (if using LangSmith)
40
+ if config.tracing.enabled and config.tracing.backend == "langsmith":
41
+ if os.environ.get("LANGCHAIN_API_KEY"):
42
+ print("✓ LANGCHAIN_API_KEY present")
43
+ else:
44
+ msg = "LANGCHAIN_API_KEY is not set (required when tracing.backend=langsmith)"
45
+ print(f"✗ {msg}")
46
+ errors.append(msg)
47
+
48
+ if not errors:
49
+ print("✓ All checks passed")
50
+ return not errors
51
+
52
+
53
+ def main() -> None:
54
+ """CLI entry point."""
55
+ parser = argparse.ArgumentParser(prog="agentbase", description="AgentBase CLI")
56
+ sub = parser.add_subparsers(dest="command")
57
+
58
+ val = sub.add_parser("validate", help="Validate agentbase.yaml and agents.yaml")
59
+ val.add_argument(
60
+ "config", nargs="?", default="agentbase.yaml", help="Path to agentbase.yaml"
61
+ )
62
+ val.add_argument(
63
+ "agents", nargs="?", default="agents.yaml", help="Path to agents.yaml"
64
+ )
65
+
66
+ args = parser.parse_args()
67
+
68
+ if args.command == "validate":
69
+ ok = _validate(args.config, args.agents)
70
+ sys.exit(0 if ok else 1)
71
+ else:
72
+ parser.print_help()
73
+ sys.exit(1)
agentbase/config.py ADDED
@@ -0,0 +1,115 @@
1
+ import os
2
+ import re
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ import yaml
7
+
8
+ from agentbase.exceptions import ConfigValidationError
9
+ from agentbase.types import AgentBaseConfig, AgentBaseDefaults, ModelAliasConfig, TracingConfig
10
+
11
+ _config_cache: dict[str, AgentBaseConfig] = {}
12
+ _ENV_VAR_RE = re.compile(r"\$\{([^}]+)\}")
13
+
14
+
15
+ def _interpolate(value: Any) -> Any:
16
+ if isinstance(value, str):
17
+
18
+ def _sub(m: re.Match) -> str:
19
+ var = m.group(1)
20
+ if var not in os.environ:
21
+ raise ConfigValidationError(
22
+ f"Environment variable '{var}' is referenced in agentbase.yaml but not set. "
23
+ f"Set '{var}' in your environment before starting the application."
24
+ )
25
+ return os.environ[var]
26
+
27
+ return _ENV_VAR_RE.sub(_sub, value)
28
+ if isinstance(value, dict):
29
+ return {k: _interpolate(v) for k, v in value.items()}
30
+ if isinstance(value, list):
31
+ return [_interpolate(v) for v in value]
32
+ return value
33
+
34
+
35
+ def _parse_models(raw_models: dict) -> dict[str, ModelAliasConfig]:
36
+ result = {}
37
+ for alias, raw in raw_models.items():
38
+ if not isinstance(raw, dict):
39
+ raise ConfigValidationError(
40
+ f"Model alias '{alias}' must be a YAML mapping in agentbase.yaml."
41
+ )
42
+ alias_dict = dict(raw)
43
+ default_params = alias_dict.pop("default_params", {})
44
+ if not alias_dict:
45
+ raise ConfigValidationError(
46
+ f"Model alias '{alias}' has no environment mappings in agentbase.yaml."
47
+ )
48
+ result[alias] = ModelAliasConfig(environments=alias_dict, default_params=default_params)
49
+ return result
50
+
51
+
52
+ def _validate_coverage(models: dict[str, ModelAliasConfig]) -> None:
53
+ all_envs: set[str] = set()
54
+ for alias_config in models.values():
55
+ all_envs.update(alias_config.environments.keys())
56
+ for alias, alias_config in models.items():
57
+ missing = all_envs - set(alias_config.environments)
58
+ if missing:
59
+ raise ConfigValidationError(
60
+ f"Model alias '{alias}' is missing mappings for environment(s): "
61
+ f"{', '.join(sorted(missing))}. "
62
+ f"Add these under 'models.{alias}' in agentbase.yaml."
63
+ )
64
+
65
+
66
+ def load_config(path: str | Path | None = None) -> AgentBaseConfig:
67
+ resolved = Path(path).resolve() if path else (Path.cwd() / "agentbase.yaml").resolve()
68
+ cache_key = str(resolved)
69
+
70
+ if cache_key in _config_cache:
71
+ return _config_cache[cache_key]
72
+
73
+ if not resolved.exists():
74
+ raise ConfigValidationError(
75
+ f"Config file not found: {resolved}. Create agentbase.yaml in your project root."
76
+ )
77
+
78
+ with open(resolved) as f:
79
+ raw = yaml.safe_load(f)
80
+
81
+ if not isinstance(raw, dict):
82
+ raise ConfigValidationError("agentbase.yaml must be a YAML mapping at the top level.")
83
+
84
+ raw = _interpolate(raw)
85
+
86
+ for field in ("project", "models"):
87
+ if field not in raw:
88
+ raise ConfigValidationError(f"agentbase.yaml is missing required field '{field}'.")
89
+
90
+ models = _parse_models(raw["models"])
91
+
92
+ defaults_raw = raw.get("defaults", {})
93
+ defaults = AgentBaseDefaults(**defaults_raw) if defaults_raw else AgentBaseDefaults()
94
+
95
+ tracing_raw = raw.get("tracing", {})
96
+ try:
97
+ tracing = TracingConfig(**tracing_raw) if tracing_raw else TracingConfig()
98
+ except Exception as e:
99
+ raise ConfigValidationError(f"Invalid 'tracing' section in agentbase.yaml: {e}") from e
100
+
101
+ config = AgentBaseConfig(
102
+ project=raw["project"],
103
+ models=models,
104
+ defaults=defaults,
105
+ tracing=tracing,
106
+ )
107
+
108
+ _validate_coverage(config.models)
109
+
110
+ _config_cache[cache_key] = config
111
+ return config
112
+
113
+
114
+ def get_environment(config: AgentBaseConfig) -> str:
115
+ return os.environ.get("AGENTBASE_ENV", config.defaults.environment)
@@ -0,0 +1,36 @@
1
+ class AgentBaseError(Exception):
2
+ pass
3
+
4
+
5
+ class ConfigValidationError(AgentBaseError):
6
+ def __init__(self, message: str) -> None:
7
+ super().__init__(message)
8
+
9
+
10
+ class AgentNotRegisteredError(AgentBaseError):
11
+ def __init__(self, agent_name: str) -> None:
12
+ super().__init__(
13
+ f"Agent '{agent_name}' is not registered in agents.yaml. "
14
+ f"Add an entry for '{agent_name}' with all required contract fields."
15
+ )
16
+ self.agent_name = agent_name
17
+
18
+
19
+ class ContractViolationError(AgentBaseError):
20
+ def __init__(self, agent_name: str, missing_field: str) -> None:
21
+ super().__init__(
22
+ f"Agent '{agent_name}' is missing required contract field '{missing_field}'. "
23
+ f"Add '{missing_field}' to the '{agent_name}' entry in agents.yaml."
24
+ )
25
+ self.agent_name = agent_name
26
+ self.missing_field = missing_field
27
+
28
+
29
+ class ModelResolutionError(AgentBaseError):
30
+ def __init__(self, alias: str, environment: str) -> None:
31
+ super().__init__(
32
+ f"Model alias '{alias}' has no mapping for environment '{environment}'. "
33
+ f"Add a '{environment}' entry under 'models.{alias}' in agentbase.yaml."
34
+ )
35
+ self.alias = alias
36
+ self.environment = environment
agentbase/models.py ADDED
@@ -0,0 +1,33 @@
1
+ from agentbase.config import get_environment
2
+ from agentbase.exceptions import ModelResolutionError
3
+ from agentbase.registry import get_agent
4
+ from agentbase.tracing import build_trace_metadata
5
+ from agentbase.types import AgentBaseConfig, AgentMetadata, ResolvedAgent
6
+
7
+
8
+ def resolve_model(agent: AgentMetadata, config: AgentBaseConfig, environment: str) -> str | None:
9
+ if agent.model_alias is None:
10
+ return None
11
+ alias_config = config.models[agent.model_alias]
12
+ if environment not in alias_config.environments:
13
+ raise ModelResolutionError(agent.model_alias, environment)
14
+ return alias_config.environments[environment]
15
+
16
+
17
+ def resolve(
18
+ agent_name: str,
19
+ registry: dict[str, AgentMetadata],
20
+ config: AgentBaseConfig,
21
+ environment: str | None = None,
22
+ ) -> ResolvedAgent:
23
+ env = environment or get_environment(config)
24
+ agent = get_agent(agent_name, registry)
25
+ model_id = resolve_model(agent, config, env)
26
+ default_params = config.models[agent.model_alias].default_params if agent.model_alias else {}
27
+ trace_metadata = build_trace_metadata(agent, model_id)
28
+ return ResolvedAgent(
29
+ agent_name=agent.agent_name,
30
+ model_id=model_id,
31
+ default_params=default_params,
32
+ trace_metadata=trace_metadata,
33
+ )
agentbase/registry.py ADDED
@@ -0,0 +1,88 @@
1
+ from pathlib import Path
2
+
3
+ import yaml
4
+
5
+ from agentbase.exceptions import (
6
+ AgentNotRegisteredError,
7
+ ConfigValidationError,
8
+ ContractViolationError,
9
+ )
10
+ from agentbase.types import AgentBaseConfig, AgentMetadata
11
+
12
+ _CONTRACT_FIELDS = ("agent_name", "agent_version", "model_alias", "prompt_ref", "owner", "status")
13
+
14
+ _registry_cache: dict[str, dict[str, AgentMetadata]] = {}
15
+
16
+
17
+ def _validate_agent(raw: dict, config: AgentBaseConfig, environment: str) -> AgentMetadata:
18
+ agent_name = raw.get("agent_name") or "<unknown>"
19
+
20
+ for field in _CONTRACT_FIELDS:
21
+ if field == "model_alias":
22
+ continue
23
+ if not raw.get(field):
24
+ raise ContractViolationError(agent_name, field)
25
+
26
+ model_alias = raw.get("model_alias")
27
+ if model_alias is not None and model_alias not in config.models:
28
+ raise ConfigValidationError(
29
+ f"Agent '{agent_name}' references model alias '{model_alias}' which is not defined "
30
+ f"in agentbase.yaml. Available aliases: {', '.join(sorted(config.models))}."
31
+ )
32
+
33
+ return AgentMetadata(
34
+ project=config.project,
35
+ environment=environment,
36
+ agent_name=raw["agent_name"],
37
+ agent_version=str(raw["agent_version"]),
38
+ model_alias=raw.get("model_alias"),
39
+ prompt_ref=raw["prompt_ref"],
40
+ owner=raw["owner"],
41
+ status=raw["status"],
42
+ )
43
+
44
+
45
+ def load_registry(
46
+ path: str | Path,
47
+ config: AgentBaseConfig,
48
+ environment: str,
49
+ ) -> dict[str, AgentMetadata]:
50
+ resolved = Path(path).resolve()
51
+ cache_key = f"{resolved}:{environment}"
52
+
53
+ if cache_key in _registry_cache:
54
+ return _registry_cache[cache_key]
55
+
56
+ if not resolved.exists():
57
+ raise ConfigValidationError(
58
+ f"Agent registry not found: {resolved}. Create agents.yaml in your project root."
59
+ )
60
+
61
+ with open(resolved) as f:
62
+ raw = yaml.safe_load(f)
63
+
64
+ if not isinstance(raw, dict) or "agents" not in raw:
65
+ raise ConfigValidationError("agents.yaml must have a top-level 'agents' list.")
66
+
67
+ agents_raw = raw["agents"]
68
+ if not isinstance(agents_raw, list):
69
+ raise ConfigValidationError("'agents' in agents.yaml must be a list.")
70
+
71
+ registry: dict[str, AgentMetadata] = {}
72
+ for agent_raw in agents_raw:
73
+ metadata = _validate_agent(agent_raw, config, environment)
74
+ if metadata.agent_name in registry:
75
+ raise ConfigValidationError(
76
+ f"Duplicate agent name '{metadata.agent_name}' in agents.yaml. "
77
+ f"Each agent must have a unique name within a project."
78
+ )
79
+ registry[metadata.agent_name] = metadata
80
+
81
+ _registry_cache[cache_key] = registry
82
+ return registry
83
+
84
+
85
+ def get_agent(name: str, registry: dict[str, AgentMetadata]) -> AgentMetadata:
86
+ if name not in registry:
87
+ raise AgentNotRegisteredError(name)
88
+ return registry[name]
agentbase/tracing.py ADDED
@@ -0,0 +1,49 @@
1
+ import os
2
+ from typing import Any
3
+
4
+ from agentbase.exceptions import ConfigValidationError
5
+ from agentbase.types import AgentBaseConfig, AgentMetadata
6
+
7
+
8
+ def init_tracing(config: AgentBaseConfig) -> None:
9
+ tc = config.tracing
10
+ if not tc.enabled or tc.backend == "none":
11
+ return
12
+ if tc.backend == "langsmith":
13
+ _init_langsmith(config.project)
14
+ else:
15
+ raise ConfigValidationError(
16
+ f"Unknown tracing backend '{tc.backend}' in agentbase.yaml. "
17
+ f"Supported values: 'langsmith', 'none'."
18
+ )
19
+
20
+
21
+ def _init_langsmith(project: str) -> None:
22
+ if not os.environ.get("LANGCHAIN_API_KEY"):
23
+ raise ConfigValidationError(
24
+ "Missing environment variable LANGCHAIN_API_KEY "
25
+ "(required for LangSmith tracing). "
26
+ "Get a key at smith.langchain.com and set LANGCHAIN_API_KEY in your environment."
27
+ )
28
+ os.environ["LANGCHAIN_TRACING_V2"] = "true"
29
+ os.environ["LANGCHAIN_PROJECT"] = project
30
+
31
+
32
+ def build_trace_metadata(agent: AgentMetadata, model_id: str | None) -> dict[str, Any]:
33
+ metadata = {
34
+ "project": agent.project,
35
+ "environment": agent.environment,
36
+ "agent_name": agent.agent_name,
37
+ "agent_version": agent.agent_version,
38
+ "owner": agent.owner,
39
+ }
40
+ if agent.model_alias is not None:
41
+ metadata["model_alias"] = agent.model_alias
42
+ if model_id is not None:
43
+ metadata["model_id"] = model_id
44
+ return {
45
+ "generation_name": agent.agent_name,
46
+ "trace_name": f"{agent.project}/{agent.agent_name}",
47
+ "tags": [agent.project, agent.environment, agent.status.value],
48
+ "metadata": metadata,
49
+ }
agentbase/types.py ADDED
@@ -0,0 +1,65 @@
1
+ from enum import StrEnum
2
+ from typing import Any
3
+
4
+ from pydantic import BaseModel
5
+
6
+
7
+ class AgentStatus(StrEnum):
8
+ experimental = "experimental"
9
+ active = "active"
10
+ deprecated = "deprecated"
11
+
12
+
13
+ class AgentMetadata(BaseModel):
14
+ project: str
15
+ environment: str
16
+ agent_name: str
17
+ agent_version: str
18
+ model_alias: str | None = None
19
+ prompt_ref: str
20
+ owner: str
21
+ status: AgentStatus
22
+
23
+
24
+ class ModelAliasConfig(BaseModel):
25
+ environments: dict[str, str]
26
+ default_params: dict[str, Any] = {}
27
+
28
+
29
+ class AgentBaseDefaults(BaseModel):
30
+ environment: str = "dev"
31
+ model_alias: str = "smart"
32
+
33
+
34
+ class TracingConfig(BaseModel):
35
+ backend: str = "none" # "langsmith" | "none"
36
+ enabled: bool = True
37
+
38
+
39
+ class AgentBaseConfig(BaseModel):
40
+ project: str
41
+ models: dict[str, ModelAliasConfig]
42
+ defaults: AgentBaseDefaults = AgentBaseDefaults()
43
+ tracing: TracingConfig = TracingConfig()
44
+
45
+ @property
46
+ def environments(self) -> set[str]:
47
+ envs: set[str] = set()
48
+ for alias_config in self.models.values():
49
+ envs.update(alias_config.environments.keys())
50
+ return envs
51
+
52
+
53
+ class ResolvedAgent(BaseModel):
54
+ agent_name: str
55
+ model_id: str | None
56
+ default_params: dict[str, Any] = {}
57
+ trace_metadata: dict[str, Any] = {}
58
+
59
+ def langchain_config(self) -> dict[str, Any]:
60
+ """RunnableConfig dict — pass as config= to any LangChain call."""
61
+ return {
62
+ "run_name": self.trace_metadata.get("generation_name", self.agent_name),
63
+ "tags": self.trace_metadata.get("tags", []),
64
+ "metadata": self.trace_metadata.get("metadata", {}),
65
+ }
@@ -0,0 +1,313 @@
1
+ Metadata-Version: 2.4
2
+ Name: ak-agentbase
3
+ Version: 0.1.0a1
4
+ Summary: Unified AI agent configuration, observability, and model management for multi-agent applications
5
+ Project-URL: Homepage, https://github.com/Shivam1904/AgentBase
6
+ Project-URL: Documentation, https://github.com/Shivam1904/AgentBase/blob/main/README.md
7
+ Project-URL: Repository, https://github.com/Shivam1904/AgentBase
8
+ Project-URL: Issues, https://github.com/Shivam1904/AgentBase/issues
9
+ Author-email: Shivam Srivastava <shivam.sriv93@gmail.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: agents,ai,configuration,langsmith,llm,observability
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: pydantic==2.12.5
22
+ Requires-Dist: pyyaml==6.0.3
23
+ Provides-Extra: dev
24
+ Requires-Dist: build==1.2.2; extra == 'dev'
25
+ Requires-Dist: pytest-cov==7.1.0; extra == 'dev'
26
+ Requires-Dist: pytest==9.0.3; extra == 'dev'
27
+ Requires-Dist: ruff==0.15.12; extra == 'dev'
28
+ Requires-Dist: twine==6.1.0; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # AgentBase v0.1-alpha
32
+
33
+ **Unified AI agent configuration, observability, and model management for production multi-agent systems.**
34
+
35
+ AgentBase is a lightweight, framework-agnostic control plane that standardizes how AI-heavy products configure models, register agents, and emit observability metadata. No framework dependencies. No vendor lock-in. Just YAML + Python.
36
+
37
+ ## Status
38
+
39
+ ✅ **v0.1-alpha** — Core library complete. 96 tests passing. First projects integrated (Writerverse, Skill-Deck pipeline).
40
+
41
+ ## What It Does
42
+
43
+ ```python
44
+ import agentbase
45
+ from langchain_openai import ChatOpenAI
46
+
47
+ # Load configuration
48
+ agentbase.load_config("agentbase.yaml", "agents.yaml")
49
+ agentbase.init_tracing()
50
+
51
+ # Resolve an agent
52
+ resolved = agentbase.resolve("my_analyzer")
53
+
54
+ # Construct LLM with AgentBase config
55
+ llm = ChatOpenAI(model=resolved.model_id, **resolved.default_params)
56
+
57
+ # Enable LangSmith tracing
58
+ result = llm.invoke(messages, config=resolved.langchain_config())
59
+ ```
60
+
61
+ **Returns:**
62
+ - `model_id`: Resolved model name (e.g., `gpt-4o`, `claude-sonnet-4-20250514`)
63
+ - `default_params`: Model config (temperature, max_tokens, etc.)
64
+ - `langchain_config()`: LangSmith trace metadata (tags, generation_name, metadata)
65
+
66
+ ## Core Features
67
+
68
+ - **YAML Config** — Single source of truth for all model aliases and agent definitions
69
+ - **Model Aliases** — `fast`, `smart`, `creative` that resolve to provider-specific IDs per environment
70
+ - **Agent Registry** — Centralized agent metadata (owner, version, prompt location, status)
71
+ - **LangSmith Integration** — Automatic trace metadata in every chain/workflow
72
+ - **Environment Switching** — Dev/prod model aliases in one config file
73
+ - **Zero Framework Dependencies** — Works with LangChain, Anthropic SDK, OpenAI SDK, etc.
74
+ - **CLI Validation** — `agentbase validate` checks config and resolves all agents
75
+
76
+ ## Installation
77
+
78
+ ```bash
79
+ pip install agentbase==0.1.0a1
80
+ ```
81
+
82
+ ## Quick Start
83
+
84
+ **1. Create config files:**
85
+
86
+ `agentbase.yaml`:
87
+ ```yaml
88
+ project: my-project
89
+
90
+ models:
91
+ fast:
92
+ dev: gpt-4o-mini
93
+ production: gpt-4o
94
+ smart:
95
+ dev: gpt-4o
96
+ production: claude-sonnet-4-20250514
97
+
98
+ defaults:
99
+ environment: dev
100
+ model_alias: smart
101
+
102
+ tracing:
103
+ backend: langsmith
104
+ enabled: true
105
+ ```
106
+
107
+ `agents.yaml`:
108
+ ```yaml
109
+ agents:
110
+ - agent_name: analyzer
111
+ model_alias: fast
112
+ owner: team
113
+ status: active
114
+ agent_version: "0.1.0"
115
+ prompt_ref: "git:backend/prompts/analyze.py"
116
+ description: "Analyzes input data"
117
+ ```
118
+
119
+ **2. Set environment variables:**
120
+
121
+ ```bash
122
+ LANGCHAIN_API_KEY=lsv2_pt_xxxxx
123
+ OPENAI_API_KEY=sk-xxxxx
124
+ ```
125
+
126
+ **3. Initialize in your app:**
127
+
128
+ ```python
129
+ import agentbase
130
+
131
+ agentbase.load_config(config_path="agentbase.yaml", registry_path="agents.yaml")
132
+ agentbase.init_tracing()
133
+ ```
134
+
135
+ **4. Use in your chains:**
136
+
137
+ ```python
138
+ resolved = agentbase.resolve("analyzer")
139
+ llm = ChatOpenAI(model=resolved.model_id, **resolved.default_params)
140
+ result = chain.invoke(input, config=resolved.langchain_config())
141
+ ```
142
+
143
+ See [QUICKSTART.md](QUICKSTART.md) for full setup instructions.
144
+
145
+ ## Architecture
146
+
147
+ ```
148
+ agentbase.load_config()
149
+ ├─ Loads agentbase.yaml (project, models, defaults, tracing)
150
+ ├─ Loads agents.yaml (agent registry)
151
+ └─ Validates all required fields
152
+
153
+ agentbase.init_tracing()
154
+ └─ Sets LANGCHAIN_TRACING_V2 and LANGCHAIN_PROJECT env vars
155
+ (if LangSmith backend is enabled)
156
+
157
+ agentbase.resolve(agent_name)
158
+ ├─ Looks up agent in registry
159
+ ├─ Resolves model_alias → model_id (using defaults.environment)
160
+ ├─ Returns ResolvedAgent with:
161
+ │ ├─ model_id (e.g., "gpt-4o")
162
+ │ ├─ default_params (temperature, max_tokens, etc.)
163
+ │ ├─ langchain_config() → LangSmith metadata
164
+ │ └─ trace_metadata → raw metadata dict
165
+ └─ (Throws ConfigValidationError if not found or invalid)
166
+ ```
167
+
168
+ ## API
169
+
170
+ ### Core Functions
171
+
172
+ ```python
173
+ agentbase.load_config(config_path: str, registry_path: str) → None
174
+ # Load and validate agentbase.yaml and agents.yaml
175
+ # Raises ConfigValidationError if validation fails
176
+
177
+ agentbase.init_tracing() → None
178
+ # Set up LangSmith tracing if enabled in config
179
+ # Requires LANGCHAIN_API_KEY env var if using LangSmith
180
+
181
+ agentbase.resolve(agent_name: str) → ResolvedAgent
182
+ # Resolve agent and return config
183
+ # Returns ResolvedAgent with model_id, default_params, langchain_config()
184
+ # Raises ConfigValidationError if agent not found
185
+
186
+ agentbase.validate(config_path: str, registry_path: str) → None
187
+ # Validation-only mode (no tracing setup)
188
+ # Used by CLI and tests
189
+ ```
190
+
191
+ ### ResolvedAgent
192
+
193
+ ```python
194
+ @dataclass
195
+ class ResolvedAgent:
196
+ model_id: str | None # "gpt-4o", "claude-sonnet-4-20250514", None for workflows
197
+ default_params: dict[str, Any] # {"temperature": 0.7, "max_tokens": 4096}
198
+
199
+ def langchain_config(self) -> dict[str, Any]:
200
+ # Returns LangSmith-compatible config for chain.invoke(config=...)
201
+ # Fields: generation_name, trace_name, tags, metadata
202
+ ```
203
+
204
+ ## CLI
205
+
206
+ ```bash
207
+ # Validate config (exit 0 if valid, 1 if errors)
208
+ agentbase validate agentbase.yaml agents.yaml
209
+
210
+ # Resolve an agent (pretty-print)
211
+ agentbase resolve agent_name --config agentbase.yaml --registry agents.yaml
212
+
213
+ # Version
214
+ agentbase --version
215
+ ```
216
+
217
+ ## Configuration Reference
218
+
219
+ ### agentbase.yaml
220
+
221
+ ```yaml
222
+ project: <string> # Project name (used in LangSmith traces)
223
+
224
+ models:
225
+ <alias>: # e.g., "fast", "smart", "creative"
226
+ dev: <model_id> # e.g., "gpt-4o-mini"
227
+ production: <model_id> # e.g., "gpt-4o"
228
+
229
+ defaults:
230
+ environment: <env> # "dev" or "production"
231
+ model_alias: <alias> # Default model alias
232
+ [other defaults...]
233
+
234
+ tracing:
235
+ backend: langsmith | none # "langsmith" or "none"
236
+ enabled: true | false # Enable/disable tracing
237
+ ```
238
+
239
+ ### agents.yaml
240
+
241
+ ```yaml
242
+ agents:
243
+ - agent_name: <string> # Unique agent identifier
244
+ model_alias: <string> # References models.* in agentbase.yaml
245
+ owner: <string> # Team or person responsible
246
+ status: active | inactive # Status
247
+ agent_version: <semver> # e.g., "0.1.0"
248
+ prompt_ref: <string> # Reference to prompt (e.g., "git:path/to/file.py")
249
+ description: <string> # Human-readable description
250
+ ```
251
+
252
+ ## Environment Variables
253
+
254
+ | Variable | Required | Purpose |
255
+ |----------|----------|---------|
256
+ | `LANGCHAIN_API_KEY` | If tracing enabled | LangSmith API key |
257
+ | `OPENAI_API_KEY` | If using gpt-* models | OpenAI API key |
258
+ | `ANTHROPIC_API_KEY` | If using claude-* models | Anthropic API key |
259
+
260
+ ## Why AgentBase?
261
+
262
+ **Before AgentBase:**
263
+ - Each project had its own config system (get_ai_config, hardcoded model names)
264
+ - LLM setup was duplicated across chains
265
+ - No unified observability or cost tracking
266
+ - Difficult to switch models for A/B testing
267
+
268
+ **With AgentBase:**
269
+ - One config file per project, YAML-based
270
+ - Agents registered once, resolved anywhere
271
+ - Automatic LangSmith integration
272
+ - Model switching via `defaults.environment`
273
+ - Cost visibility per agent per environment
274
+
275
+ ## For Projects Using AgentBase
276
+
277
+ - **Writerverse** — 35 agents + 7 LangGraph workflows (scene generation, consistency analysis, chat, supervisor)
278
+ - **Skill-Deck** — Q&A system with dynamic model routing
279
+ - **Shivam Portfolio** — Proof-of-concept (first integration)
280
+
281
+ See [QUICKSTART.md](QUICKSTART.md) for project integration steps.
282
+
283
+ ## For Publishing
284
+
285
+ See [PUBLISH.md](PUBLISH.md) for detailed steps to publish to PyPI.
286
+
287
+ ## Development
288
+
289
+ ```bash
290
+ # Install dev dependencies
291
+ pip install -e .[dev]
292
+
293
+ # Run tests
294
+ pytest tests/ -v
295
+
296
+ # Lint
297
+ ruff check agentbase/ tests/
298
+
299
+ # Format
300
+ ruff format agentbase/ tests/
301
+ ```
302
+
303
+ ## License
304
+
305
+ MIT — see [LICENSE](LICENSE)
306
+
307
+ ## Author
308
+
309
+ Shivam Srivastava — [GitHub](https://github.com/Shivam1904)
310
+
311
+ ---
312
+
313
+ **Questions?** Open an issue on [GitHub](https://github.com/Shivam1904/AgentBase/issues)
@@ -0,0 +1,14 @@
1
+ agentbase/__init__.py,sha256=ioI1ZEwDA3Q4DvbBAIjyJs1gjHJ_MKy30z3zH_seS7Y,2115
2
+ agentbase/__main__.py,sha256=YJ2L3vBKgUG0jAY6Sh4nfwiaFceLOU2k5zgbUp-zdlU,39
3
+ agentbase/cli.py,sha256=-WN4nxdm-g3aZzP7Bv-wWTVroWzt22hn77GPgmunBHI,2337
4
+ agentbase/config.py,sha256=SAh2A_c9WGCqSh-h4bN3E3ouKdoIntb0xSPVz4ou-es,3916
5
+ agentbase/exceptions.py,sha256=yLNJA2kRbea49ES_nT7vjy-Knze3WJB0s6Bl7DwOOVo,1279
6
+ agentbase/models.py,sha256=atK9HNGM25c4JkvPrNxw3aLgFXg29RrvVerK9B300xg,1275
7
+ agentbase/registry.py,sha256=7Xpf_g13_bPzefP7QKHWk0oQOT33l5oW32ATf4v2hTQ,2885
8
+ agentbase/tracing.py,sha256=XcOXdh6DwKbOeSxyQE8C3B3bALH7fzbisuUJA5GmTEU,1676
9
+ agentbase/types.py,sha256=EBM6Z3l7JS23jiHYANbsmUf5E5uEjfjdx3zDNVGUUPM,1649
10
+ ak_agentbase-0.1.0a1.dist-info/METADATA,sha256=I6MhNCrpPcGjirVUBIJ3tGB6YKRI8NzrZGfvEL_BfYY,9179
11
+ ak_agentbase-0.1.0a1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
12
+ ak_agentbase-0.1.0a1.dist-info/entry_points.txt,sha256=ikSlYfVSxSUSwFN0ZbMFMioyuC2mviQCm-ZaZwFPPOE,49
13
+ ak_agentbase-0.1.0a1.dist-info/licenses/LICENSE,sha256=f3ihmqRSYLpC8ZEWx7Ns0gvqWJnMPCMpS_iKz0ngNhc,1074
14
+ ak_agentbase-0.1.0a1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ agentbase = agentbase.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shivam Srivastava
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.