digital-employee-core 0.0.1__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.
Files changed (19) hide show
  1. digital_employee_core-0.0.1/PKG-INFO +103 -0
  2. digital_employee_core-0.0.1/README.md +74 -0
  3. digital_employee_core-0.0.1/digital_employee_core/__init__.py +26 -0
  4. digital_employee_core-0.0.1/digital_employee_core/agents/__init__.py +1 -0
  5. digital_employee_core-0.0.1/digital_employee_core/config_templates/__init__.py +8 -0
  6. digital_employee_core-0.0.1/digital_employee_core/config_templates/loader.py +187 -0
  7. digital_employee_core-0.0.1/digital_employee_core/config_templates/mcp_configs.yaml +54 -0
  8. digital_employee_core-0.0.1/digital_employee_core/config_templates/tool_configs.yaml +9 -0
  9. digital_employee_core-0.0.1/digital_employee_core/configuration/__init__.py +7 -0
  10. digital_employee_core-0.0.1/digital_employee_core/configuration/configuration.py +27 -0
  11. digital_employee_core-0.0.1/digital_employee_core/connectors/__init__.py +4 -0
  12. digital_employee_core-0.0.1/digital_employee_core/connectors/mcps/__init__.py +22 -0
  13. digital_employee_core-0.0.1/digital_employee_core/connectors/mcps/google_mcps.py +62 -0
  14. digital_employee_core-0.0.1/digital_employee_core/connectors/tools/__init__.py +1 -0
  15. digital_employee_core-0.0.1/digital_employee_core/digital_employee/__init__.py +8 -0
  16. digital_employee_core-0.0.1/digital_employee_core/digital_employee/digital_employee.py +367 -0
  17. digital_employee_core-0.0.1/digital_employee_core/identity/__init__.py +16 -0
  18. digital_employee_core-0.0.1/digital_employee_core/identity/identity.py +83 -0
  19. digital_employee_core-0.0.1/pyproject.toml +64 -0
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.4
2
+ Name: digital-employee-core
3
+ Version: 0.0.1
4
+ Summary:
5
+ Author: Digital Employee Team
6
+ Requires-Python: >=3.11,<3.13
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.11
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Requires-Dist: aip-agents-binary (>=0.4.8,<0.5.0)
11
+ Requires-Dist: apscheduler (>=3.11.0,<4.0.0)
12
+ Requires-Dist: authlib (>=1.6.1,<2.0.0)
13
+ Requires-Dist: email-validator (>=2.3.0,<3.0.0)
14
+ Requires-Dist: fastapi (>=0.117.1,<0.118.0)
15
+ Requires-Dist: glaip-sdk (>=0.6.2)
16
+ Requires-Dist: gllm-plugin-binary (>=0.0.7,<0.0.8)
17
+ Requires-Dist: google-api-python-client (>=2.0.0,<3.0.0)
18
+ Requires-Dist: opentelemetry-instrumentation-httpx
19
+ Requires-Dist: opentelemetry-instrumentation-requests
20
+ Requires-Dist: pre-commit (>=4.2.0,<5.0.0)
21
+ Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
22
+ Requires-Dist: python-multipart (>=0.0.20,<0.0.21)
23
+ Requires-Dist: pyyaml (>=6.0.3,<7.0.0)
24
+ Requires-Dist: requests (>=2.32.0,<3.0.0)
25
+ Requires-Dist: ruff (>=0.14.0,<0.15.0)
26
+ Requires-Dist: uvicorn (>=0.34.0,<0.35.0)
27
+ Description-Content-Type: text/markdown
28
+
29
+ # Digital Employee Core
30
+
31
+ A Python library for building and managing AI-powered digital employees with support for tools, MCPs (Model Context Protocol), and flexible configuration management.
32
+
33
+ ## Setup
34
+
35
+ ### 1. Install Dependencies
36
+
37
+ ```bash
38
+ poetry install
39
+ ```
40
+
41
+ ### 2. Configure Environment
42
+
43
+ Copy the example environment file and add your credentials:
44
+
45
+ ```bash
46
+ cp .env.example .env
47
+ ```
48
+
49
+ Edit `.env` with your configuration:
50
+
51
+ ```bash
52
+ # Required
53
+ AIP_API_URL=https://your-ai-platform-url.com
54
+ AIP_API_KEY=your-api-key
55
+
56
+ # Optional: Google MCP Configuration
57
+ GOOGLE_CALENDAR_MCP_URL=https://api.example.com/calendar/mcp
58
+ GOOGLE_DOCS_MCP_URL=https://api.example.com/docs/mcp
59
+ GOOGLE_MCP_X_API_KEY=your-google-mcp-key
60
+ ```
61
+
62
+ ## Run Examples
63
+
64
+ ### Basic Usage
65
+
66
+ ```bash
67
+ poetry run python examples/basic_usage.py
68
+ ```
69
+
70
+ ### Configuration Usage
71
+
72
+ ```bash
73
+ poetry run python examples/configuration_usage.py
74
+ ```
75
+
76
+ ### Subclass Example
77
+
78
+ ```bash
79
+ poetry run python examples/subclass_example.py
80
+ ```
81
+
82
+ ## Run Tests
83
+
84
+ ```bash
85
+ # Run all tests
86
+ poetry run pytest
87
+
88
+ # Run with verbose output
89
+ poetry run pytest -v
90
+
91
+ # Run with coverage
92
+ poetry run coverage run -m pytest
93
+ poetry run coverage report
94
+
95
+ # Run with coverage HTML report
96
+ poetry run coverage run -m pytest
97
+ poetry run coverage html
98
+ # Open htmlcov/index.html in browser
99
+
100
+ # Run specific test file
101
+ poetry run pytest tests/digital_employee/test_digital_employee.py
102
+ ```
103
+
@@ -0,0 +1,74 @@
1
+ # Digital Employee Core
2
+
3
+ A Python library for building and managing AI-powered digital employees with support for tools, MCPs (Model Context Protocol), and flexible configuration management.
4
+
5
+ ## Setup
6
+
7
+ ### 1. Install Dependencies
8
+
9
+ ```bash
10
+ poetry install
11
+ ```
12
+
13
+ ### 2. Configure Environment
14
+
15
+ Copy the example environment file and add your credentials:
16
+
17
+ ```bash
18
+ cp .env.example .env
19
+ ```
20
+
21
+ Edit `.env` with your configuration:
22
+
23
+ ```bash
24
+ # Required
25
+ AIP_API_URL=https://your-ai-platform-url.com
26
+ AIP_API_KEY=your-api-key
27
+
28
+ # Optional: Google MCP Configuration
29
+ GOOGLE_CALENDAR_MCP_URL=https://api.example.com/calendar/mcp
30
+ GOOGLE_DOCS_MCP_URL=https://api.example.com/docs/mcp
31
+ GOOGLE_MCP_X_API_KEY=your-google-mcp-key
32
+ ```
33
+
34
+ ## Run Examples
35
+
36
+ ### Basic Usage
37
+
38
+ ```bash
39
+ poetry run python examples/basic_usage.py
40
+ ```
41
+
42
+ ### Configuration Usage
43
+
44
+ ```bash
45
+ poetry run python examples/configuration_usage.py
46
+ ```
47
+
48
+ ### Subclass Example
49
+
50
+ ```bash
51
+ poetry run python examples/subclass_example.py
52
+ ```
53
+
54
+ ## Run Tests
55
+
56
+ ```bash
57
+ # Run all tests
58
+ poetry run pytest
59
+
60
+ # Run with verbose output
61
+ poetry run pytest -v
62
+
63
+ # Run with coverage
64
+ poetry run coverage run -m pytest
65
+ poetry run coverage report
66
+
67
+ # Run with coverage HTML report
68
+ poetry run coverage run -m pytest
69
+ poetry run coverage html
70
+ # Open htmlcov/index.html in browser
71
+
72
+ # Run specific test file
73
+ poetry run pytest tests/digital_employee/test_digital_employee.py
74
+ ```
@@ -0,0 +1,26 @@
1
+ """Digital employee Core package.
2
+
3
+ This package provides the core functionality for building and managing
4
+ digital employees with support for tools, MCPs, and configurations.
5
+ """
6
+
7
+ from digital_employee_core.config_templates import ConfigTemplateLoader
8
+ from digital_employee_core.configuration import DigitalEmployeeConfiguration
9
+ from digital_employee_core.digital_employee import DigitalEmployee
10
+ from digital_employee_core.identity import (
11
+ DigitalEmployeeIdentity,
12
+ DigitalEmployeeJob,
13
+ DigitalEmployeeSupervisor,
14
+ )
15
+
16
+ __all__ = [
17
+ # Core classes
18
+ "ConfigTemplateLoader",
19
+ "DigitalEmployee",
20
+ # Identity classes
21
+ "DigitalEmployeeIdentity",
22
+ "DigitalEmployeeJob",
23
+ "DigitalEmployeeSupervisor",
24
+ # Configuration classes
25
+ "DigitalEmployeeConfiguration",
26
+ ]
@@ -0,0 +1 @@
1
+ """Agents package for digital employee core."""
@@ -0,0 +1,8 @@
1
+ """Configuration template utilities.
2
+
3
+ This module provides utilities for loading and processing configuration templates.
4
+ """
5
+
6
+ from digital_employee_core.config_templates.loader import ConfigTemplateLoader
7
+
8
+ __all__ = ["ConfigTemplateLoader"]
@@ -0,0 +1,187 @@
1
+ """Configuration template loader and processor.
2
+
3
+ This module provides utilities for loading and processing configuration templates
4
+ from YAML files, replacing placeholders with values from DigitalEmployeeConfiguration objects.
5
+
6
+ Authors:
7
+ Immanuel Rhesa (immanuel.rhesa@gdplabs.id)
8
+
9
+ References:
10
+ NONE
11
+ """
12
+
13
+ import re
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ import yaml
18
+
19
+ from digital_employee_core.configuration.configuration import DigitalEmployeeConfiguration
20
+
21
+ # Configuration template filenames
22
+ MCP_CONFIGS_TEMPLATE = "mcp_configs.yaml"
23
+ TOOL_CONFIGS_TEMPLATE = "tool_configs.yaml"
24
+
25
+
26
+ class ConfigTemplateLoader:
27
+ """Loads and processes configuration templates from YAML files.
28
+
29
+ This class handles loading YAML configuration templates and replacing
30
+ placeholders with actual values from DigitalEmployeeConfiguration objects.
31
+
32
+ Attributes:
33
+ template_dir (Path): Path to the directory containing template files.
34
+ """
35
+
36
+ def __init__(self, template_dir: str | Path | None = None):
37
+ """Initialize the ConfigTemplateLoader.
38
+
39
+ Args:
40
+ template_dir (str | Path | None, optional): Path to template directory. Defaults to config_templates
41
+ in the package directory.
42
+ """
43
+ if template_dir is None:
44
+ # Default to config_templates directory in the package
45
+ package_dir = Path(__file__).parent.parent
46
+ template_dir = package_dir / "config_templates"
47
+
48
+ self.template_dir = Path(template_dir)
49
+
50
+ def load_template(self, filename: str) -> dict[str, Any]:
51
+ """Load a YAML template file.
52
+
53
+ Args:
54
+ filename (str): Name of the template file (e.g., 'mcp_configs.yaml').
55
+
56
+ Returns:
57
+ dict[str, Any]: Dictionary containing the template configuration.
58
+
59
+ Raises:
60
+ FileNotFoundError: If the template file does not exist.
61
+ """
62
+ template_path = self.template_dir / filename
63
+ return self.load_template_from_path(template_path)
64
+
65
+ def load_template_from_path(self, filepath: str | Path) -> dict[str, Any]:
66
+ """Load a YAML template file from an absolute path.
67
+
68
+ Args:
69
+ filepath (str | Path): Absolute path to the template file.
70
+
71
+ Returns:
72
+ dict[str, Any]: Dictionary containing the template configuration.
73
+
74
+ Raises:
75
+ FileNotFoundError: If the template file does not exist.
76
+ """
77
+ template_path = Path(filepath)
78
+ if not template_path.exists():
79
+ raise FileNotFoundError(f"Template file not found: {template_path}")
80
+
81
+ with open(template_path) as f:
82
+ return yaml.safe_load(f) or {}
83
+
84
+ @staticmethod
85
+ def merge_configs(
86
+ base_config: dict[str, dict[str, Any]],
87
+ additional_config: dict[str, dict[str, Any]],
88
+ ) -> dict[str, dict[str, Any]]:
89
+ """Merge two configuration dictionaries.
90
+
91
+ The additional_config will override values in base_config for matching keys.
92
+ New keys in additional_config will be added to the result.
93
+ Performs recursive deep merge for nested dictionaries.
94
+
95
+ Args:
96
+ base_config (dict[str, dict[str, Any]]): Base configuration dictionary.
97
+ additional_config (dict[str, dict[str, Any]]): Additional configuration to merge.
98
+
99
+ Returns:
100
+ dict[str, dict[str, Any]]: Merged configuration dictionary.
101
+ """
102
+
103
+ def _deep_merge(base: dict[str, Any], additional: dict[str, Any]) -> dict[str, Any]:
104
+ """Recursively merge two dictionaries."""
105
+ result = base.copy()
106
+ for key, value in additional.items():
107
+ if key in result and isinstance(result[key], dict) and isinstance(value, dict):
108
+ # Recursively merge nested dictionaries
109
+ result[key] = _deep_merge(result[key], value)
110
+ else:
111
+ # Override or add new key
112
+ result[key] = value
113
+ return result
114
+
115
+ return _deep_merge(base_config, additional_config)
116
+
117
+ def _replace_placeholders(
118
+ self,
119
+ template: dict[str, Any],
120
+ configurations: list[DigitalEmployeeConfiguration],
121
+ ) -> dict[str, Any]:
122
+ """Replace placeholders in template with configuration values.
123
+
124
+ Placeholders are in format <PLACEHOLDER_KEY> and are replaced with
125
+ values from DigitalEmployeeConfiguration objects matching the key.
126
+
127
+ Args:
128
+ template (dict[str, Any]): Template dictionary with placeholders.
129
+ configurations (list[DigitalEmployeeConfiguration]): List of DigitalEmployeeConfiguration objects.
130
+
131
+ Returns:
132
+ dict[str, Any]: Dictionary with placeholders replaced by actual values.
133
+ """
134
+ # Create a mapping of keys to values from configurations
135
+ config_map: dict[str, str] = {}
136
+ for config in configurations:
137
+ config_map[config.key] = config.value
138
+
139
+ def replace_value(value: Any) -> Any:
140
+ """Recursively replace placeholders in values."""
141
+ if isinstance(value, str):
142
+ # Find all placeholders in format <KEY>
143
+ placeholders = re.findall(r"<([^>]+)>", value)
144
+ result = value
145
+ for placeholder in placeholders:
146
+ if placeholder in config_map:
147
+ # Replace the placeholder with the actual value
148
+ result = result.replace(f"<{placeholder}>", config_map[placeholder])
149
+ return result
150
+ elif isinstance(value, dict):
151
+ return {k: replace_value(v) for k, v in value.items()}
152
+ elif isinstance(value, list):
153
+ return [replace_value(item) for item in value]
154
+ else:
155
+ return value
156
+
157
+ return replace_value(template)
158
+
159
+ def load_mcp_configs(
160
+ self,
161
+ configurations: list[DigitalEmployeeConfiguration],
162
+ ) -> dict[str, dict[str, Any]]:
163
+ """Load MCP configuration template and replace placeholders.
164
+
165
+ Args:
166
+ configurations (list[DigitalEmployeeConfiguration]): List of DigitalEmployeeConfiguration objects.
167
+
168
+ Returns:
169
+ dict[str, dict[str, Any]]: Dictionary mapping MCP names to their configurations.
170
+ """
171
+ template = self.load_template(MCP_CONFIGS_TEMPLATE)
172
+ return self._replace_placeholders(template, configurations)
173
+
174
+ def load_tool_configs(
175
+ self,
176
+ configurations: list[DigitalEmployeeConfiguration],
177
+ ) -> dict[str, dict[str, Any]]:
178
+ """Load tool configuration template and replace placeholders.
179
+
180
+ Args:
181
+ configurations (list[DigitalEmployeeConfiguration]): List of DigitalEmployeeConfiguration objects.
182
+
183
+ Returns:
184
+ dict[str, dict[str, Any]]: Dictionary mapping tool names to their configurations.
185
+ """
186
+ template = self.load_template(TOOL_CONFIGS_TEMPLATE)
187
+ return self._replace_placeholders(template, configurations)
@@ -0,0 +1,54 @@
1
+ # MCP Configuration Templates
2
+ #
3
+ # This file contains configuration templates for MCPs (Model Context Protocol servers).
4
+ # Placeholders in angle brackets (e.g., <PLACEHOLDER_KEY>) will be replaced with values
5
+ # from DigitalEmployeeConfiguration objects at runtime using the placeholder key.
6
+ #
7
+ # Format:
8
+ # mcp-name:
9
+ # config:
10
+ # key: value or <PLACEHOLDER_KEY>
11
+ # authentication:
12
+ # type: "api-key" | "oauth" | "bearer"
13
+ # key: "header-name"
14
+ # value: <PLACEHOLDER_KEY>
15
+
16
+ digital_employee_google_calendar_mcp:
17
+ config:
18
+ url: <GOOGLE_CALENDAR_MCP_URL>
19
+ authentication:
20
+ type: api-key
21
+ key: X-API-Key
22
+ value: <GOOGLE_MCP_X_API_KEY>
23
+
24
+ digital_employee_google_docs_mcp:
25
+ config:
26
+ url: <GOOGLE_DOCS_MCP_URL>
27
+ authentication:
28
+ type: api-key
29
+ key: X-API-Key
30
+ value: <GOOGLE_MCP_X_API_KEY>
31
+
32
+ digital_employee_google_drive_mcp:
33
+ config:
34
+ url: <GOOGLE_DRIVE_MCP_URL>
35
+ authentication:
36
+ type: api-key
37
+ key: X-API-Key
38
+ value: <GOOGLE_MCP_X_API_KEY>
39
+
40
+ digital_employee_google_mail_mcp:
41
+ config:
42
+ url: <GOOGLE_MAIL_MCP_URL>
43
+ authentication:
44
+ type: api-key
45
+ key: X-API-Key
46
+ value: <GOOGLE_MCP_X_API_KEY>
47
+
48
+ digital_employee_google_sheets_mcp:
49
+ config:
50
+ url: <GOOGLE_SHEETS_MCP_URL>
51
+ authentication:
52
+ type: api-key
53
+ key: X-API-Key
54
+ value: <GOOGLE_MCP_X_API_KEY>
@@ -0,0 +1,9 @@
1
+ # Tool Configuration Templates
2
+ #
3
+ # This file contains configuration templates for tools.
4
+ # Placeholders in angle brackets (e.g., <PLACEHOLDER_KEY>) will be replaced with values
5
+ # from DigitalEmployeeConfiguration objects at runtime using the placeholder key.
6
+ #
7
+ # Format:
8
+ # tool_name:
9
+ # key: value or <PLACEHOLDER_KEY>
@@ -0,0 +1,7 @@
1
+ """Configuration management module for digital employee."""
2
+
3
+ from .configuration import DigitalEmployeeConfiguration
4
+
5
+ __all__ = [
6
+ "DigitalEmployeeConfiguration",
7
+ ]
@@ -0,0 +1,27 @@
1
+ """Configuration and credentials management for digital employee.
2
+
3
+ This module provides classes for managing configuration credentials.
4
+
5
+ Authors:
6
+ Immanuel Rhesa (immanuel.rhesa@gdplabs.id)
7
+
8
+ References:
9
+ NONE
10
+ """
11
+
12
+ from dataclasses import dataclass
13
+
14
+
15
+ @dataclass
16
+ class DigitalEmployeeConfiguration:
17
+ """Represents a single configuration for digital employee.
18
+
19
+ This class combines both configuration and credential information.
20
+
21
+ Attributes:
22
+ key (str): The configuration key identifier (e.g., "GOOGLE_MCP_X_API_KEY", "SPREADSHEET_ID").
23
+ value (str): The configuration value or credential.
24
+ """
25
+
26
+ key: str
27
+ value: str
@@ -0,0 +1,4 @@
1
+ """Connectors module for digital employee Core.
2
+
3
+ This module contains various connectors and integration components.
4
+ """
@@ -0,0 +1,22 @@
1
+ """MCPs package for Digital Employee Core.
2
+
3
+ This package provides pre-configured MCP instances for various services.
4
+ """
5
+
6
+ from digital_employee_core.connectors.mcps.google_mcps import (
7
+ ALL_GOOGLE_MCPS,
8
+ google_calendar_mcp,
9
+ google_docs_mcp,
10
+ google_drive_mcp,
11
+ google_mail_mcp,
12
+ google_sheets_mcp,
13
+ )
14
+
15
+ __all__ = [
16
+ "google_calendar_mcp",
17
+ "google_docs_mcp",
18
+ "google_drive_mcp",
19
+ "google_mail_mcp",
20
+ "google_sheets_mcp",
21
+ "ALL_GOOGLE_MCPS",
22
+ ]
@@ -0,0 +1,62 @@
1
+ """Google MCPs for Digital Employee Core.
2
+
3
+ This module provides pre-configured MCP instances for Google services
4
+ that can be easily imported and used in Digital Employee implementations.
5
+
6
+ Authors:
7
+ Immanuel Rhesa (immanuel.rhesa@gdplabs.id)
8
+
9
+ References:
10
+ NONE
11
+ """
12
+
13
+ from glaip_sdk import MCP
14
+
15
+ # Google Calendar MCP
16
+ google_calendar_mcp = MCP(
17
+ name="digital_employee_google_calendar_mcp",
18
+ description="MCP for Google Calendar Operation for DE",
19
+ transport="http",
20
+ config={"url": "https://default.com/google_calendar/mcp"},
21
+ )
22
+
23
+ # Google Docs MCP
24
+ google_docs_mcp = MCP(
25
+ name="digital_employee_google_docs_mcp",
26
+ description="MCP for Google Docs Operation for DE",
27
+ transport="http",
28
+ config={"url": "https://default.com/google_docs/mcp"},
29
+ )
30
+
31
+ # Google Drive MCP
32
+ google_drive_mcp = MCP(
33
+ name="digital_employee_google_drive_mcp",
34
+ description="MCP for Google Drive Operation for DE",
35
+ transport="http",
36
+ config={"url": "https://default.com/google_drive/mcp"},
37
+ )
38
+
39
+ # Google Mail MCP
40
+ google_mail_mcp = MCP(
41
+ name="digital_employee_google_mail_mcp",
42
+ description="MCP for Google Mail Operation for DE",
43
+ transport="http",
44
+ config={"url": "https://default.com/google_mail/mcp"},
45
+ )
46
+
47
+ # Google Sheets MCP
48
+ google_sheets_mcp = MCP(
49
+ name="digital_employee_google_sheets_mcp",
50
+ description="MCP for Google Sheets Operation for DE",
51
+ transport="http",
52
+ config={"url": "https://default.com/google_sheets/mcp"},
53
+ )
54
+
55
+ # Convenience list of all Google MCPs
56
+ ALL_GOOGLE_MCPS = [
57
+ google_calendar_mcp,
58
+ google_docs_mcp,
59
+ google_drive_mcp,
60
+ google_mail_mcp,
61
+ google_sheets_mcp,
62
+ ]
@@ -0,0 +1 @@
1
+ """Tools package for digital employee core."""
@@ -0,0 +1,8 @@
1
+ """Digital Employee module.
2
+
3
+ This module provides the main DigitalEmployee orchestrator class.
4
+ """
5
+
6
+ from digital_employee_core.digital_employee.digital_employee import DigitalEmployee
7
+
8
+ __all__ = ["DigitalEmployee"]
@@ -0,0 +1,367 @@
1
+ """Core digital employee orchestrator.
2
+
3
+ This module provides the base DigitalEmployee class that manages agents,
4
+ tools, and MCPs. It implements the ConfigBuilder interface to
5
+ provide configuration building capabilities.
6
+
7
+ Authors:
8
+ Immanuel Rhesa (immanuel.rhesa@gdplabs.id)
9
+
10
+ References:
11
+ NONE
12
+ """
13
+
14
+ from typing import Any
15
+
16
+ from glaip_sdk import MCP, Agent, Tool
17
+
18
+ from digital_employee_core.config_templates.loader import ConfigTemplateLoader
19
+ from digital_employee_core.configuration.configuration import (
20
+ DigitalEmployeeConfiguration,
21
+ )
22
+ from digital_employee_core.identity.identity import DigitalEmployeeIdentity
23
+
24
+
25
+ class DigitalEmployee:
26
+ """Core digital employee orchestrator.
27
+
28
+ This class manages the lifecycle of a digital employee, including
29
+ initialization, deployment, and execution of agents with tools and MCPs.
30
+ Provides methods for building tool and MCP configurations from YAML templates.
31
+
32
+ Attributes:
33
+ identity (DigitalEmployeeIdentity): The digital employee's identity.
34
+ tools (list[Tool]): List of tools the digital employee can use.
35
+ sub_agents (list[Agent]): List of sub-agents (for future use).
36
+ mcps (list[MCP]): List of MCPs the digital employee can use.
37
+ configurations (list[DigitalEmployeeConfiguration]): List of configuration objects for tools and MCPs.
38
+ agent (Agent | None): The main agent instance.
39
+ config_loader (ConfigTemplateLoader): The configuration template loader instance.
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ identity: DigitalEmployeeIdentity,
45
+ tools: list[Tool] | None = None,
46
+ sub_agents: list[Agent] | None = None,
47
+ mcps: list[MCP] | None = None,
48
+ configurations: list[DigitalEmployeeConfiguration] | None = None,
49
+ ):
50
+ """Initialize the digital employee.
51
+
52
+ Args:
53
+ identity (DigitalEmployeeIdentity): The digital employee's identity.
54
+ tools (list[Tool] | None, optional): List of tools the digital employee can use. Defaults to None.
55
+ sub_agents (list[Agent] | None, optional): List of sub-agents (for future use). Defaults to None.
56
+ mcps (list[MCP] | None, optional): List of MCPs the digital employee can use. Defaults to None.
57
+ configurations (list[DigitalEmployeeConfiguration] | None, optional): List of configuration objects
58
+ for tools and MCPs. Defaults to None.
59
+ """
60
+ self.identity = identity
61
+ self.tools = tools or []
62
+ self.sub_agents = sub_agents or []
63
+ self.mcps = mcps or []
64
+ self.configurations = configurations or []
65
+ self.agent: Agent | None = None
66
+ self.config_loader = ConfigTemplateLoader()
67
+
68
+ def add_tools(self, tools: list[Tool]) -> None:
69
+ """Add tools to the digital employee.
70
+
71
+ Args:
72
+ tools (list[Tool]): List of tools to add.
73
+ """
74
+ self.tools.extend(tools)
75
+
76
+ def add_mcps(self, mcps: list[MCP]) -> None:
77
+ """Add MCPs to the digital employee.
78
+
79
+ Args:
80
+ mcps (list[MCP]): List of MCPs to add.
81
+ """
82
+ self.mcps.extend(mcps)
83
+
84
+ def add_sub_agents(self, sub_agents: list[Agent]) -> None:
85
+ """Add sub-agents to the digital employee.
86
+
87
+ Args:
88
+ sub_agents (list[Agent]): List of sub-agents to add.
89
+ """
90
+ self.sub_agents.extend(sub_agents)
91
+
92
+ def build_instruction(self) -> str:
93
+ """Build the instruction/prompt for the agent.
94
+
95
+ This method can be overridden by subclasses to provide custom
96
+ instructions based on the digital employee's identity and job.
97
+ Always includes the digital employee's name, email, and language preferences
98
+ in the prompt to ensure identity awareness.
99
+
100
+ Returns:
101
+ str: The instruction string for the agent.
102
+ """
103
+ # Build identity information with language
104
+ language_names = [lang.value for lang in self.identity.languages]
105
+
106
+ if len(language_names) > 1:
107
+ language_list = ", ".join(language_names[:-1]) + f" and {language_names[-1]}"
108
+ language_instruction = (
109
+ f"You must respond only in the following languages: {language_list}. "
110
+ f"Choose the most appropriate language based on the user's input or context."
111
+ )
112
+ else:
113
+ language_instruction = f"You must respond only in {language_names[0]} language."
114
+
115
+ identity_prefix = (
116
+ f"You are {self.identity.name} ({self.identity.email}), {self.identity.job.title}. "
117
+ f"{language_instruction}"
118
+ )
119
+
120
+ return f"{identity_prefix}\n\n{self.identity.job.instruction}"
121
+
122
+ def deploy(self) -> None:
123
+ """Deploy the digital employee.
124
+
125
+ This method initializes the agent with tools and MCPs,
126
+ applies configurations to MCPs and tools, and deploys it to the AI platform.
127
+ """
128
+ if self.configurations:
129
+ tool_configs = self.build_tool_config(self.configurations)
130
+ mcp_configs = self.build_mcp_config(self.configurations)
131
+ configured_mcps = self._apply_mcp_configs(self.mcps, mcp_configs)
132
+ configured_tools = self._apply_tool_configs(self.tools, tool_configs)
133
+ else:
134
+ configured_mcps = self.mcps
135
+ configured_tools = self.tools
136
+
137
+ self.agent = Agent(
138
+ name=self.identity.name,
139
+ description=self.identity.job.description,
140
+ instruction=self.build_instruction(),
141
+ tools=configured_tools,
142
+ agents=self.sub_agents,
143
+ mcps=configured_mcps,
144
+ )
145
+
146
+ self.agent.deploy()
147
+
148
+ def run(
149
+ self,
150
+ message: str,
151
+ configurations: list[DigitalEmployeeConfiguration] | None = None,
152
+ ) -> str:
153
+ """Run the digital employee with a message.
154
+
155
+ Args:
156
+ message (str): The message/prompt to send to the digital employee.
157
+ configurations (list[DigitalEmployeeConfiguration] | None, optional): List of configuration objects.
158
+ Defaults to None.
159
+
160
+ Returns:
161
+ str: The agent's response as a string.
162
+
163
+ Raises:
164
+ RuntimeError: If the agent has not been deployed.
165
+ """
166
+ if self.agent is None:
167
+ raise RuntimeError("Agent has not been deployed. Call deploy() before run().")
168
+
169
+ configurations = configurations or []
170
+ runtime_config = self._build_runtime_configs(configurations)
171
+
172
+ return self.agent.run(message=message, runtime_config=runtime_config)
173
+
174
+ def build_tool_config(self, configurations: list[DigitalEmployeeConfiguration]) -> dict[str, dict[str, Any]]:
175
+ """Build tool configuration based on identity and configurations.
176
+
177
+ This method loads tool configuration templates from YAML and replaces
178
+ placeholders with values from DigitalEmployeeConfiguration objects. Subclasses can
179
+ override this to provide custom logic and merge with parent configs.
180
+
181
+ Example for subclasses:
182
+ def build_tool_config(self, configurations):
183
+ # Get base configs from parent
184
+ base_configs = super().build_tool_config(configurations)
185
+
186
+ # Load additional configs for this subclass
187
+ additional = self.config_loader.load_tool_configs(configurations)
188
+
189
+ # Merge them
190
+ return self.config_loader.merge_configs(base_configs, additional)
191
+
192
+ Args:
193
+ configurations: List of configuration objects.
194
+
195
+ Returns:
196
+ dict[str, dict[str, Any]]: Dictionary mapping tool names to their configuration dictionaries.
197
+ """
198
+ return self.config_loader.load_tool_configs(configurations)
199
+
200
+ def build_mcp_config(self, configurations: list[DigitalEmployeeConfiguration]) -> dict[str, dict[str, Any]]:
201
+ """Build MCP configuration based on identity and configurations.
202
+
203
+ This method loads MCP configuration templates from YAML and replaces
204
+ placeholders with values from DigitalEmployeeConfiguration objects. Subclasses can
205
+ override this to provide custom logic and merge with parent configs.
206
+
207
+ Example for subclasses:
208
+ def build_mcp_config(self, configurations):
209
+ # Get base configs from parent
210
+ base_configs = super().build_mcp_config(configurations)
211
+
212
+ # Load additional configs for this subclass
213
+ additional = self.config_loader.load_mcp_configs(configurations)
214
+
215
+ # Merge them
216
+ return self.config_loader.merge_configs(base_configs, additional)
217
+
218
+ Args:
219
+ configurations (list[DigitalEmployeeConfiguration]): List of configuration objects.
220
+
221
+ Returns:
222
+ dict[str, dict[str, Any]]: Dictionary mapping MCP names to their configuration dictionaries.
223
+ """
224
+ return self.config_loader.load_mcp_configs(configurations)
225
+
226
+ def _apply_mcp_configs(
227
+ self,
228
+ mcps: list[MCP],
229
+ mcp_configs: dict[str, dict[str, Any]],
230
+ ) -> list[MCP]:
231
+ """Apply configurations to MCPs.
232
+
233
+ This method updates the config and authentication attributes of MCPs
234
+ based on the provided configuration dictionary. Only updates MCPs
235
+ that have matching configurations.
236
+
237
+ Args:
238
+ mcps (list[MCP]): List of MCP instances.
239
+ mcp_configs (dict[str, dict[str, Any]]): Dictionary mapping MCP names to their configurations.
240
+
241
+ Returns:
242
+ list[MCP]: List of MCPs with configurations applied.
243
+ """
244
+ return [self._apply_single_mcp_config(mcp, mcp_configs) for mcp in mcps]
245
+
246
+ def _apply_single_mcp_config(self, mcp: MCP, mcp_configs: dict[str, dict[str, Any]]) -> MCP:
247
+ """Apply configuration to a single MCP.
248
+
249
+ Args:
250
+ mcp (MCP): MCP instance to configure.
251
+ mcp_configs (dict[str, dict[str, Any]]): Dictionary mapping MCP names to their configurations.
252
+
253
+ Returns:
254
+ MCP: MCP with configuration applied.
255
+ """
256
+ if not (mcp.name and mcp.name in mcp_configs):
257
+ return mcp
258
+
259
+ config_data = mcp_configs[mcp.name]
260
+
261
+ if config_data.get("config"):
262
+ if mcp.config is None:
263
+ mcp.config = {}
264
+ mcp.config.update(config_data["config"])
265
+
266
+ if config_data.get("authentication"):
267
+ if mcp.authentication is None:
268
+ mcp.authentication = {}
269
+ mcp.authentication.update(config_data["authentication"])
270
+
271
+ return mcp
272
+
273
+ def _apply_tool_configs(
274
+ self,
275
+ tools: list[Tool],
276
+ tool_configs: dict[str, dict[str, Any]],
277
+ ) -> list[Tool]:
278
+ """Apply configurations to tools.
279
+
280
+ This method updates tool configurations based on the provided
281
+ configuration dictionary. Only updates tools that have matching
282
+ configurations and a config attribute.
283
+
284
+ Args:
285
+ tools (list[Tool]): List of tool names (strings) or Tool objects.
286
+ tool_configs (dict[str, dict[str, Any]]): Dictionary mapping tool names to their configurations.
287
+
288
+ Returns:
289
+ list[Tool]: List of tools with configurations applied.
290
+ """
291
+ configured_tools = []
292
+ for tool in tools:
293
+ if hasattr(tool, "name") and hasattr(tool, "config"):
294
+ if tool.name and tool.name in tool_configs:
295
+ config_data = tool_configs[tool.name]
296
+ if tool.config is None:
297
+ tool.config = {}
298
+ tool.config.update(config_data)
299
+
300
+ configured_tools.append(tool)
301
+
302
+ return configured_tools
303
+
304
+ def _filter_tool_configs(
305
+ self,
306
+ all_tool_configs: dict[str, dict[str, Any]],
307
+ ) -> dict[str, dict[str, Any]]:
308
+ """Filter tool configs to only include tools used by the agent.
309
+
310
+ Args:
311
+ all_tool_configs (dict[str, dict[str, Any]]): All available tool configurations.
312
+
313
+ Returns:
314
+ dict[str, dict[str, Any]]: Filtered tool configurations for tools used by the agent.
315
+ """
316
+ return {
317
+ name: config
318
+ for name, config in all_tool_configs.items()
319
+ if any(
320
+ (hasattr(tool, "name") and tool.name == name) or (isinstance(tool, str) and tool == name)
321
+ for tool in self.agent.tools
322
+ )
323
+ }
324
+
325
+ def _filter_mcp_configs(
326
+ self,
327
+ all_mcp_configs: dict[str, dict[str, Any]],
328
+ ) -> dict[str, dict[str, Any]]:
329
+ """Filter MCP configs to only include MCPs used by the agent.
330
+
331
+ Args:
332
+ all_mcp_configs (dict[str, dict[str, Any]]): All available MCP configurations.
333
+
334
+ Returns:
335
+ dict[str, dict[str, Any]]: Filtered MCP configurations for MCPs used by the agent.
336
+ """
337
+ return {
338
+ name: config
339
+ for name, config in all_mcp_configs.items()
340
+ if any(
341
+ (hasattr(mcp, "name") and mcp.name == name) or (isinstance(mcp, str) and mcp == name)
342
+ for mcp in self.agent.mcps
343
+ )
344
+ }
345
+
346
+ def _build_runtime_configs(
347
+ self,
348
+ configurations: list[DigitalEmployeeConfiguration],
349
+ ) -> dict[str, dict[str, dict[str, Any]]]:
350
+ """Build runtime configurations for tools and MCPs.
351
+
352
+ This method builds all configurations and filters them to only include
353
+ tools and MCPs that are actually used by the agent.
354
+
355
+ Args:
356
+ configurations (list[DigitalEmployeeConfiguration]): List of configuration objects.
357
+
358
+ Returns:
359
+ dict[str, dict[str, dict[str, Any]]]: Dictionary with 'tool_configs' and 'mcp_configs' keys.
360
+ """
361
+ all_tool_configs = self.build_tool_config(configurations)
362
+ all_mcp_configs = self.build_mcp_config(configurations)
363
+
364
+ return {
365
+ "tool_configs": self._filter_tool_configs(all_tool_configs),
366
+ "mcp_configs": self._filter_mcp_configs(all_mcp_configs),
367
+ }
@@ -0,0 +1,16 @@
1
+ """Identity module for digital employee.
2
+
3
+ This module provides identity classes for digital employees.
4
+ """
5
+
6
+ from digital_employee_core.identity.identity import (
7
+ DigitalEmployeeIdentity,
8
+ DigitalEmployeeJob,
9
+ DigitalEmployeeSupervisor,
10
+ )
11
+
12
+ __all__ = [
13
+ "DigitalEmployeeIdentity",
14
+ "DigitalEmployeeJob",
15
+ "DigitalEmployeeSupervisor",
16
+ ]
@@ -0,0 +1,83 @@
1
+ """Digital employee identity classes.
2
+
3
+ This module provides identity classes for digital employees with support for
4
+ different levels of identity (base, HR, and specific implementations).
5
+
6
+ Authors:
7
+ Immanuel Rhesa (immanuel.rhesa@gdplabs.id)
8
+
9
+ References:
10
+ NONE
11
+ """
12
+
13
+ from enum import StrEnum
14
+ from typing import Annotated
15
+
16
+ from pydantic import BaseModel, EmailStr, Field, StringConstraints
17
+
18
+
19
+ class LangId(StrEnum):
20
+ """The language id.
21
+
22
+ Attributes:
23
+ EN: Id for English language.
24
+ ID: Id for Indonesian language.
25
+ """
26
+
27
+ EN = "en"
28
+ ID = "id"
29
+
30
+
31
+ class DigitalEmployeeJob(BaseModel):
32
+ """Digital employee Job definition.
33
+
34
+ Encapsulates job-specific information for a digital employee,
35
+ including title, description, and instructions.
36
+
37
+ Attributes:
38
+ title (str): The job title or role name (e.g., "HR Assistant", "Payroll Specialist").
39
+ description (str): Detailed description of the job and responsibilities.
40
+ instruction (str): Specific instructions or guidelines for performing the job.
41
+ """
42
+
43
+ title: str
44
+ description: str
45
+ instruction: str
46
+
47
+
48
+ class DigitalEmployeeSupervisor(BaseModel):
49
+ """Digital employee Supervisor information.
50
+
51
+ Represents the supervisor or manager overseeing the digital employee.
52
+
53
+ Attributes:
54
+ name (str): The supervisor's full name.
55
+ email (EmailStr): The supervisor's email address. Must be a valid email format.
56
+ """
57
+
58
+ name: str
59
+ email: EmailStr
60
+
61
+
62
+ class DigitalEmployeeIdentity(BaseModel):
63
+ """Base digital employee Identity.
64
+
65
+ This is the base identity class that provides comprehensive information
66
+ for a digital employee, including personal details, job role, and supervisor.
67
+
68
+ Attributes:
69
+ name (Annotated[str, StringConstraints(max_length=100)]): The name of the digital employee.
70
+ Maximum length is 100 characters.
71
+ email (EmailStr): The digital employee's email address. Must be a valid email format.
72
+ job (DigitalEmployeeJob): The job information (title, description, instruction).
73
+ supervisor (DigitalEmployeeSupervisor | None, optional): The supervisor information (name and email).
74
+ Defaults to None.
75
+ languages (list[LangId]): The list of supported languages for the digital employee.
76
+ Defaults to [LangId.EN].
77
+ """
78
+
79
+ name: Annotated[str, StringConstraints(max_length=100)]
80
+ email: EmailStr
81
+ job: DigitalEmployeeJob
82
+ supervisor: DigitalEmployeeSupervisor | None = None
83
+ languages: list[LangId] = Field(default_factory=lambda: [LangId.EN])
@@ -0,0 +1,64 @@
1
+ [tool.poetry]
2
+ name = "digital-employee-core"
3
+ version = "0.0.1"
4
+ description = ""
5
+ authors = ["Digital Employee Team"]
6
+ readme = "README.md"
7
+
8
+
9
+ [tool.poetry.dependencies]
10
+ python = ">=3.11,<3.13"
11
+
12
+ fastapi = ">=0.117.1,<0.118.0"
13
+ uvicorn = ">=0.34.0,<0.35.0"
14
+
15
+ authlib = "^1.6.1"
16
+ google-api-python-client = "^2.0.0"
17
+ python-multipart = "^0.0.20"
18
+ opentelemetry-instrumentation-requests = "*"
19
+ opentelemetry-instrumentation-httpx = "*"
20
+ apscheduler = "^3.11.0"
21
+ python-dotenv = "^1.0.0"
22
+ requests = "^2.32.0"
23
+ pre-commit = "^4.2.0"
24
+ glaip-sdk = ">=0.6.2"
25
+ ruff = "^0.14.0"
26
+
27
+ aip-agents-binary = "^0.4.8"
28
+ gllm-plugin-binary = "^0.0.7"
29
+ pyyaml = "^6.0.3"
30
+ email-validator = "^2.3.0"
31
+
32
+ [tool.poetry.group.dev.dependencies]
33
+ coverage = "^7.11.0"
34
+ pytest = "^8.4.2"
35
+ pytest-xdist = "^3.8.0"
36
+ black = "^25.1.0"
37
+ flake8 = "^7.3.0"
38
+ faker = "^37.12.0"
39
+ pytest-asyncio = "^1.2.0"
40
+
41
+ [[tool.poetry.source]]
42
+ name = "gen-ai-internal"
43
+ url = "https://asia-southeast2-python.pkg.dev/gdp-labs/gen-ai-internal/simple/"
44
+ priority = "supplemental"
45
+
46
+ [build-system]
47
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
48
+ build-backend = "poetry.core.masonry.api"
49
+
50
+ [tool.pytest.ini_options]
51
+ norecursedirs = ["docker/*", "pgdata"]
52
+
53
+ [tool.ruff]
54
+ line-length = 120
55
+
56
+ [tool.ruff.lint]
57
+ ignore = ["B008", "C901", "E266"]
58
+ select = ["B", "B9", "C", "D", "E", "F", "I", "PL", "W"]
59
+
60
+ [tool.ruff.lint.per-file-ignores]
61
+ "tests/*" = ["D", "PLR", "ARG"]
62
+
63
+ [tool.ruff.lint.pydocstyle]
64
+ convention = "google"