dacp 0.3.0__tar.gz → 0.3.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.
- {dacp-0.3.0 → dacp-0.3.1}/PKG-INFO +96 -1
- {dacp-0.3.0 → dacp-0.3.1}/README.md +95 -0
- {dacp-0.3.0 → dacp-0.3.1}/dacp/__init__.py +22 -1
- {dacp-0.3.0 → dacp-0.3.1}/dacp/intelligence.py +128 -22
- dacp-0.3.1/dacp/logging_config.py +128 -0
- dacp-0.3.1/dacp/orchestrator.py +248 -0
- dacp-0.3.1/dacp/tools.py +86 -0
- {dacp-0.3.0 → dacp-0.3.1}/dacp.egg-info/PKG-INFO +96 -1
- {dacp-0.3.0 → dacp-0.3.1}/dacp.egg-info/SOURCES.txt +1 -0
- {dacp-0.3.0 → dacp-0.3.1}/pyproject.toml +1 -1
- dacp-0.3.0/dacp/orchestrator.py +0 -260
- dacp-0.3.0/dacp/tools.py +0 -55
- {dacp-0.3.0 → dacp-0.3.1}/LICENSE +0 -0
- {dacp-0.3.0 → dacp-0.3.1}/dacp/exceptions.py +0 -0
- {dacp-0.3.0 → dacp-0.3.1}/dacp/llm.py +0 -0
- {dacp-0.3.0 → dacp-0.3.1}/dacp/main.py +0 -0
- {dacp-0.3.0 → dacp-0.3.1}/dacp/protocol.py +0 -0
- {dacp-0.3.0 → dacp-0.3.1}/dacp/types.py +0 -0
- {dacp-0.3.0 → dacp-0.3.1}/dacp.egg-info/dependency_links.txt +0 -0
- {dacp-0.3.0 → dacp-0.3.1}/dacp.egg-info/requires.txt +0 -0
- {dacp-0.3.0 → dacp-0.3.1}/dacp.egg-info/top_level.txt +0 -0
- {dacp-0.3.0 → dacp-0.3.1}/setup.cfg +0 -0
- {dacp-0.3.0 → dacp-0.3.1}/tests/test_intelligence.py +0 -0
- {dacp-0.3.0 → dacp-0.3.1}/tests/test_llm.py +0 -0
- {dacp-0.3.0 → dacp-0.3.1}/tests/test_orchestrator.py +0 -0
- {dacp-0.3.0 → dacp-0.3.1}/tests/test_protocol.py +0 -0
- {dacp-0.3.0 → dacp-0.3.1}/tests/test_tools.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: dacp
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.1
|
4
4
|
Summary: Declarative Agent Communication Protocol - A protocol for managing LLM/agent communications and tool function calls
|
5
5
|
Author-email: Andrew Whitehouse <andrew.whitehouse@example.com>
|
6
6
|
License: MIT
|
@@ -132,6 +132,16 @@ response = dacp.call_llm("What is the weather like today?")
|
|
132
132
|
|
133
133
|
- `call_llm(prompt: str, model: str = "gpt-4") -> str`: Call OpenAI (legacy function)
|
134
134
|
|
135
|
+
### Logging
|
136
|
+
|
137
|
+
- `enable_info_logging(log_file: str = None) -> None`: Enable info-level logging with emoji format
|
138
|
+
- `enable_debug_logging(log_file: str = None) -> None`: Enable debug logging with detailed format
|
139
|
+
- `enable_quiet_logging() -> None`: Enable only error and critical logging
|
140
|
+
- `setup_dacp_logging(level, format_style, include_timestamp, log_file) -> None`: Custom logging setup
|
141
|
+
- `set_dacp_log_level(level: str) -> None`: Change log level dynamically
|
142
|
+
- `disable_dacp_logging() -> None`: Disable all DACP logging
|
143
|
+
- `enable_dacp_logging() -> None`: Re-enable DACP logging
|
144
|
+
|
135
145
|
### Protocol
|
136
146
|
|
137
147
|
- `parse_agent_response(response: str | dict) -> dict`: Parse agent response
|
@@ -348,6 +358,91 @@ else:
|
|
348
358
|
- ✅ Returns detailed success/error information
|
349
359
|
- ✅ Safe error handling
|
350
360
|
|
361
|
+
## Logging
|
362
|
+
|
363
|
+
DACP includes comprehensive logging to help you monitor agent operations, tool executions, and intelligence calls.
|
364
|
+
|
365
|
+
### Quick Setup
|
366
|
+
|
367
|
+
```python
|
368
|
+
import dacp
|
369
|
+
|
370
|
+
# Enable info-level logging with emoji format (recommended for production)
|
371
|
+
dacp.enable_info_logging()
|
372
|
+
|
373
|
+
# Enable debug logging for development (shows detailed information)
|
374
|
+
dacp.enable_debug_logging()
|
375
|
+
|
376
|
+
# Enable quiet logging (errors only)
|
377
|
+
dacp.enable_quiet_logging()
|
378
|
+
```
|
379
|
+
|
380
|
+
### Custom Configuration
|
381
|
+
|
382
|
+
```python
|
383
|
+
# Full control over logging configuration
|
384
|
+
dacp.setup_dacp_logging(
|
385
|
+
level="INFO", # DEBUG, INFO, WARNING, ERROR, CRITICAL
|
386
|
+
format_style="emoji", # "simple", "detailed", "emoji"
|
387
|
+
include_timestamp=True, # Include timestamps
|
388
|
+
log_file="dacp.log" # Optional: also log to file
|
389
|
+
)
|
390
|
+
|
391
|
+
# Change log level dynamically
|
392
|
+
dacp.set_dacp_log_level("DEBUG")
|
393
|
+
|
394
|
+
# Disable/enable logging
|
395
|
+
dacp.disable_dacp_logging()
|
396
|
+
dacp.enable_dacp_logging()
|
397
|
+
```
|
398
|
+
|
399
|
+
### What Gets Logged
|
400
|
+
|
401
|
+
With logging enabled, you'll see:
|
402
|
+
|
403
|
+
- **🎭 Agent Registration**: When agents are registered/unregistered
|
404
|
+
- **📨 Message Routing**: Messages sent to agents and broadcast operations
|
405
|
+
- **🔧 Tool Execution**: Tool calls, execution time, and results
|
406
|
+
- **🧠 Intelligence Calls**: LLM provider calls, configuration, and performance
|
407
|
+
- **❌ Errors**: Detailed error information with context
|
408
|
+
- **📊 Performance**: Execution times for operations
|
409
|
+
|
410
|
+
### Log Format Examples
|
411
|
+
|
412
|
+
**Emoji Format** (clean, production-friendly):
|
413
|
+
```
|
414
|
+
2025-07-02 09:54:58 - 🎭 Orchestrator initialized with session ID: session_1751414098
|
415
|
+
2025-07-02 09:54:58 - ✅ Agent 'demo-agent' registered successfully (type: MyAgent)
|
416
|
+
2025-07-02 09:54:58 - 📨 Sending message to agent 'demo-agent'
|
417
|
+
2025-07-02 09:54:58 - 🔧 Agent 'demo-agent' requested tool execution
|
418
|
+
2025-07-02 09:54:58 - 🛠️ Executing tool: 'file_writer' with args: {...}
|
419
|
+
2025-07-02 09:54:58 - ✅ Tool 'file_writer' executed successfully in 0.001s
|
420
|
+
```
|
421
|
+
|
422
|
+
**Detailed Format** (development/debugging):
|
423
|
+
```
|
424
|
+
2025-07-02 09:54:58 - dacp.orchestrator:89 - INFO - 📨 Sending message to agent 'demo-agent'
|
425
|
+
2025-07-02 09:54:58 - dacp.orchestrator:90 - DEBUG - 📋 Message content: {'task': 'greet'}
|
426
|
+
2025-07-02 09:54:58 - dacp.tools:26 - DEBUG - 🛠️ Executing tool 'file_writer' with args: {...}
|
427
|
+
```
|
428
|
+
|
429
|
+
### Example Usage
|
430
|
+
|
431
|
+
```python
|
432
|
+
import dacp
|
433
|
+
|
434
|
+
# Enable logging
|
435
|
+
dacp.enable_info_logging()
|
436
|
+
|
437
|
+
# Create and use components - logging happens automatically
|
438
|
+
orchestrator = dacp.Orchestrator()
|
439
|
+
agent = MyAgent()
|
440
|
+
orchestrator.register_agent("my-agent", agent)
|
441
|
+
|
442
|
+
# This will log the message sending, tool execution, etc.
|
443
|
+
response = orchestrator.send_message("my-agent", {"task": "process"})
|
444
|
+
```
|
445
|
+
|
351
446
|
## Development
|
352
447
|
|
353
448
|
```bash
|
@@ -85,6 +85,16 @@ response = dacp.call_llm("What is the weather like today?")
|
|
85
85
|
|
86
86
|
- `call_llm(prompt: str, model: str = "gpt-4") -> str`: Call OpenAI (legacy function)
|
87
87
|
|
88
|
+
### Logging
|
89
|
+
|
90
|
+
- `enable_info_logging(log_file: str = None) -> None`: Enable info-level logging with emoji format
|
91
|
+
- `enable_debug_logging(log_file: str = None) -> None`: Enable debug logging with detailed format
|
92
|
+
- `enable_quiet_logging() -> None`: Enable only error and critical logging
|
93
|
+
- `setup_dacp_logging(level, format_style, include_timestamp, log_file) -> None`: Custom logging setup
|
94
|
+
- `set_dacp_log_level(level: str) -> None`: Change log level dynamically
|
95
|
+
- `disable_dacp_logging() -> None`: Disable all DACP logging
|
96
|
+
- `enable_dacp_logging() -> None`: Re-enable DACP logging
|
97
|
+
|
88
98
|
### Protocol
|
89
99
|
|
90
100
|
- `parse_agent_response(response: str | dict) -> dict`: Parse agent response
|
@@ -301,6 +311,91 @@ else:
|
|
301
311
|
- ✅ Returns detailed success/error information
|
302
312
|
- ✅ Safe error handling
|
303
313
|
|
314
|
+
## Logging
|
315
|
+
|
316
|
+
DACP includes comprehensive logging to help you monitor agent operations, tool executions, and intelligence calls.
|
317
|
+
|
318
|
+
### Quick Setup
|
319
|
+
|
320
|
+
```python
|
321
|
+
import dacp
|
322
|
+
|
323
|
+
# Enable info-level logging with emoji format (recommended for production)
|
324
|
+
dacp.enable_info_logging()
|
325
|
+
|
326
|
+
# Enable debug logging for development (shows detailed information)
|
327
|
+
dacp.enable_debug_logging()
|
328
|
+
|
329
|
+
# Enable quiet logging (errors only)
|
330
|
+
dacp.enable_quiet_logging()
|
331
|
+
```
|
332
|
+
|
333
|
+
### Custom Configuration
|
334
|
+
|
335
|
+
```python
|
336
|
+
# Full control over logging configuration
|
337
|
+
dacp.setup_dacp_logging(
|
338
|
+
level="INFO", # DEBUG, INFO, WARNING, ERROR, CRITICAL
|
339
|
+
format_style="emoji", # "simple", "detailed", "emoji"
|
340
|
+
include_timestamp=True, # Include timestamps
|
341
|
+
log_file="dacp.log" # Optional: also log to file
|
342
|
+
)
|
343
|
+
|
344
|
+
# Change log level dynamically
|
345
|
+
dacp.set_dacp_log_level("DEBUG")
|
346
|
+
|
347
|
+
# Disable/enable logging
|
348
|
+
dacp.disable_dacp_logging()
|
349
|
+
dacp.enable_dacp_logging()
|
350
|
+
```
|
351
|
+
|
352
|
+
### What Gets Logged
|
353
|
+
|
354
|
+
With logging enabled, you'll see:
|
355
|
+
|
356
|
+
- **🎭 Agent Registration**: When agents are registered/unregistered
|
357
|
+
- **📨 Message Routing**: Messages sent to agents and broadcast operations
|
358
|
+
- **🔧 Tool Execution**: Tool calls, execution time, and results
|
359
|
+
- **🧠 Intelligence Calls**: LLM provider calls, configuration, and performance
|
360
|
+
- **❌ Errors**: Detailed error information with context
|
361
|
+
- **📊 Performance**: Execution times for operations
|
362
|
+
|
363
|
+
### Log Format Examples
|
364
|
+
|
365
|
+
**Emoji Format** (clean, production-friendly):
|
366
|
+
```
|
367
|
+
2025-07-02 09:54:58 - 🎭 Orchestrator initialized with session ID: session_1751414098
|
368
|
+
2025-07-02 09:54:58 - ✅ Agent 'demo-agent' registered successfully (type: MyAgent)
|
369
|
+
2025-07-02 09:54:58 - 📨 Sending message to agent 'demo-agent'
|
370
|
+
2025-07-02 09:54:58 - 🔧 Agent 'demo-agent' requested tool execution
|
371
|
+
2025-07-02 09:54:58 - 🛠️ Executing tool: 'file_writer' with args: {...}
|
372
|
+
2025-07-02 09:54:58 - ✅ Tool 'file_writer' executed successfully in 0.001s
|
373
|
+
```
|
374
|
+
|
375
|
+
**Detailed Format** (development/debugging):
|
376
|
+
```
|
377
|
+
2025-07-02 09:54:58 - dacp.orchestrator:89 - INFO - 📨 Sending message to agent 'demo-agent'
|
378
|
+
2025-07-02 09:54:58 - dacp.orchestrator:90 - DEBUG - 📋 Message content: {'task': 'greet'}
|
379
|
+
2025-07-02 09:54:58 - dacp.tools:26 - DEBUG - 🛠️ Executing tool 'file_writer' with args: {...}
|
380
|
+
```
|
381
|
+
|
382
|
+
### Example Usage
|
383
|
+
|
384
|
+
```python
|
385
|
+
import dacp
|
386
|
+
|
387
|
+
# Enable logging
|
388
|
+
dacp.enable_info_logging()
|
389
|
+
|
390
|
+
# Create and use components - logging happens automatically
|
391
|
+
orchestrator = dacp.Orchestrator()
|
392
|
+
agent = MyAgent()
|
393
|
+
orchestrator.register_agent("my-agent", agent)
|
394
|
+
|
395
|
+
# This will log the message sending, tool execution, etc.
|
396
|
+
response = orchestrator.send_message("my-agent", {"task": "process"})
|
397
|
+
```
|
398
|
+
|
304
399
|
## Development
|
305
400
|
|
306
401
|
```bash
|
@@ -17,22 +17,43 @@ from .tools import (
|
|
17
17
|
from .llm import call_llm
|
18
18
|
from .intelligence import invoke_intelligence
|
19
19
|
from .orchestrator import Orchestrator, Agent
|
20
|
+
from .logging_config import (
|
21
|
+
setup_dacp_logging,
|
22
|
+
enable_debug_logging,
|
23
|
+
enable_info_logging,
|
24
|
+
enable_quiet_logging,
|
25
|
+
set_dacp_log_level,
|
26
|
+
disable_dacp_logging,
|
27
|
+
enable_dacp_logging,
|
28
|
+
)
|
20
29
|
|
21
30
|
__version__ = "0.3.0"
|
22
31
|
|
23
32
|
__all__ = [
|
33
|
+
# Protocol functions
|
24
34
|
"parse_agent_response",
|
25
|
-
"is_tool_request",
|
35
|
+
"is_tool_request",
|
26
36
|
"get_tool_request",
|
27
37
|
"wrap_tool_result",
|
28
38
|
"is_final_response",
|
29
39
|
"get_final_response",
|
40
|
+
# Tool functions
|
30
41
|
"register_tool",
|
31
42
|
"run_tool",
|
32
43
|
"TOOL_REGISTRY",
|
33
44
|
"file_writer",
|
45
|
+
# LLM functions
|
34
46
|
"call_llm",
|
35
47
|
"invoke_intelligence",
|
48
|
+
# Agent orchestration
|
36
49
|
"Orchestrator",
|
37
50
|
"Agent",
|
51
|
+
# Logging configuration
|
52
|
+
"setup_dacp_logging",
|
53
|
+
"enable_debug_logging",
|
54
|
+
"enable_info_logging",
|
55
|
+
"enable_quiet_logging",
|
56
|
+
"set_dacp_log_level",
|
57
|
+
"disable_dacp_logging",
|
58
|
+
"enable_dacp_logging",
|
38
59
|
]
|
@@ -6,7 +6,8 @@ import os
|
|
6
6
|
import logging
|
7
7
|
from typing import Dict, Any, Optional
|
8
8
|
|
9
|
-
|
9
|
+
# Set up logger for this module
|
10
|
+
logger = logging.getLogger("dacp.intelligence")
|
10
11
|
|
11
12
|
|
12
13
|
class IntelligenceError(Exception):
|
@@ -42,27 +43,58 @@ def invoke_intelligence(prompt: str, config: Dict[str, Any]) -> str:
|
|
42
43
|
"""
|
43
44
|
engine = config.get("engine")
|
44
45
|
if not engine:
|
46
|
+
logger.error("❌ Missing 'engine' in intelligence configuration")
|
45
47
|
raise ConfigurationError("Missing 'engine' in intelligence configuration")
|
46
48
|
|
47
49
|
engine = engine.lower()
|
50
|
+
model = config.get("model", "default")
|
48
51
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
52
|
+
logger.info(f"🧠 Invoking intelligence: engine='{engine}', model='{model}'")
|
53
|
+
logger.debug(f"📋 Prompt length: {len(prompt)} characters")
|
54
|
+
logger.debug(f"⚙️ Full config: {_sanitize_config_for_logging(config)}")
|
55
|
+
|
56
|
+
import time
|
57
|
+
start_time = time.time()
|
58
|
+
|
59
|
+
try:
|
60
|
+
if engine == "openai":
|
61
|
+
result = _invoke_openai(prompt, config)
|
62
|
+
elif engine == "anthropic":
|
63
|
+
result = _invoke_anthropic(prompt, config)
|
64
|
+
elif engine == "azure":
|
65
|
+
result = _invoke_azure_openai(prompt, config)
|
66
|
+
elif engine == "local":
|
67
|
+
result = _invoke_local(prompt, config)
|
68
|
+
else:
|
69
|
+
logger.error(f"❌ Unsupported intelligence engine: {engine}")
|
70
|
+
raise UnsupportedProviderError(f"Unsupported intelligence engine: {engine}")
|
71
|
+
|
72
|
+
execution_time = time.time() - start_time
|
73
|
+
logger.info(f"✅ Intelligence response received in {execution_time:.3f}s (length: {len(result)} chars)")
|
74
|
+
logger.debug(f"📤 Response preview: {result[:100]}{'...' if len(result) > 100 else ''}")
|
75
|
+
|
76
|
+
return result
|
77
|
+
|
78
|
+
except (IntelligenceError, UnsupportedProviderError, ConfigurationError):
|
79
|
+
# Re-raise our own exceptions without modification
|
80
|
+
execution_time = time.time() - start_time
|
81
|
+
logger.error(f"❌ Intelligence call failed after {execution_time:.3f}s")
|
82
|
+
raise
|
83
|
+
except Exception as e:
|
84
|
+
execution_time = time.time() - start_time
|
85
|
+
logger.error(f"❌ Unexpected intelligence error after {execution_time:.3f}s: {type(e).__name__}: {e}")
|
86
|
+
raise IntelligenceError(f"Unexpected error: {e}")
|
59
87
|
|
60
88
|
|
61
89
|
def _invoke_openai(prompt: str, config: Dict[str, Any]) -> str:
|
62
90
|
"""Invoke OpenAI provider."""
|
91
|
+
logger.debug("🔵 Initializing OpenAI provider")
|
92
|
+
|
63
93
|
try:
|
64
94
|
import openai
|
95
|
+
logger.debug("✅ OpenAI package imported successfully")
|
65
96
|
except ImportError:
|
97
|
+
logger.error("❌ OpenAI package not installed")
|
66
98
|
raise IntelligenceError("OpenAI package not installed. Run: pip install openai")
|
67
99
|
|
68
100
|
model = config.get("model", "gpt-4")
|
@@ -71,11 +103,17 @@ def _invoke_openai(prompt: str, config: Dict[str, Any]) -> str:
|
|
71
103
|
temperature = config.get("temperature", 0.7)
|
72
104
|
max_tokens = config.get("max_tokens", 150)
|
73
105
|
|
106
|
+
logger.debug(f"🔧 OpenAI config: model={model}, base_url={base_url}, temp={temperature}, max_tokens={max_tokens}")
|
107
|
+
|
74
108
|
if not api_key:
|
109
|
+
logger.error("❌ OpenAI API key not found")
|
75
110
|
raise ConfigurationError("OpenAI API key not found in config or OPENAI_API_KEY environment variable")
|
76
111
|
|
77
112
|
try:
|
113
|
+
logger.debug("🔗 Creating OpenAI client")
|
78
114
|
client = openai.OpenAI(api_key=api_key, base_url=base_url)
|
115
|
+
|
116
|
+
logger.debug("📡 Sending request to OpenAI API")
|
79
117
|
response = client.chat.completions.create(
|
80
118
|
model=model,
|
81
119
|
messages=[{"role": "user", "content": prompt}],
|
@@ -85,19 +123,26 @@ def _invoke_openai(prompt: str, config: Dict[str, Any]) -> str:
|
|
85
123
|
|
86
124
|
content = response.choices[0].message.content
|
87
125
|
if content is None:
|
126
|
+
logger.error("❌ OpenAI returned empty response")
|
88
127
|
raise IntelligenceError("OpenAI returned empty response")
|
128
|
+
|
129
|
+
logger.debug(f"✅ OpenAI API call successful")
|
89
130
|
return content
|
90
131
|
|
91
132
|
except Exception as e:
|
92
|
-
|
133
|
+
logger.error(f"❌ OpenAI API error: {type(e).__name__}: {e}")
|
93
134
|
raise IntelligenceError(f"OpenAI API error: {e}")
|
94
135
|
|
95
136
|
|
96
137
|
def _invoke_anthropic(prompt: str, config: Dict[str, Any]) -> str:
|
97
138
|
"""Invoke Anthropic (Claude) provider."""
|
139
|
+
logger.debug("🟣 Initializing Anthropic provider")
|
140
|
+
|
98
141
|
try:
|
99
142
|
import anthropic
|
143
|
+
logger.debug("✅ Anthropic package imported successfully")
|
100
144
|
except ImportError:
|
145
|
+
logger.error("❌ Anthropic package not installed")
|
101
146
|
raise IntelligenceError("Anthropic package not installed. Run: pip install anthropic")
|
102
147
|
|
103
148
|
model = config.get("model", "claude-3-haiku-20240307")
|
@@ -106,11 +151,17 @@ def _invoke_anthropic(prompt: str, config: Dict[str, Any]) -> str:
|
|
106
151
|
max_tokens = config.get("max_tokens", 150)
|
107
152
|
temperature = config.get("temperature", 0.7)
|
108
153
|
|
154
|
+
logger.debug(f"🔧 Anthropic config: model={model}, base_url={base_url}, temp={temperature}, max_tokens={max_tokens}")
|
155
|
+
|
109
156
|
if not api_key:
|
157
|
+
logger.error("❌ Anthropic API key not found")
|
110
158
|
raise ConfigurationError("Anthropic API key not found in config or ANTHROPIC_API_KEY environment variable")
|
111
159
|
|
112
160
|
try:
|
161
|
+
logger.debug("🔗 Creating Anthropic client")
|
113
162
|
client = anthropic.Anthropic(api_key=api_key, base_url=base_url)
|
163
|
+
|
164
|
+
logger.debug("📡 Sending request to Anthropic API")
|
114
165
|
response = client.messages.create(
|
115
166
|
model=model,
|
116
167
|
max_tokens=max_tokens,
|
@@ -119,21 +170,28 @@ def _invoke_anthropic(prompt: str, config: Dict[str, Any]) -> str:
|
|
119
170
|
)
|
120
171
|
|
121
172
|
if not response.content or len(response.content) == 0:
|
173
|
+
logger.error("❌ Anthropic returned empty response")
|
122
174
|
raise IntelligenceError("Anthropic returned empty response")
|
123
175
|
|
124
176
|
# Anthropic returns a list of content blocks
|
125
|
-
|
177
|
+
result = response.content[0].text
|
178
|
+
logger.debug(f"✅ Anthropic API call successful")
|
179
|
+
return result
|
126
180
|
|
127
181
|
except Exception as e:
|
128
|
-
|
182
|
+
logger.error(f"❌ Anthropic API error: {type(e).__name__}: {e}")
|
129
183
|
raise IntelligenceError(f"Anthropic API error: {e}")
|
130
184
|
|
131
185
|
|
132
186
|
def _invoke_azure_openai(prompt: str, config: Dict[str, Any]) -> str:
|
133
187
|
"""Invoke Azure OpenAI provider."""
|
188
|
+
logger.debug("🔷 Initializing Azure OpenAI provider")
|
189
|
+
|
134
190
|
try:
|
135
191
|
import openai
|
192
|
+
logger.debug("✅ OpenAI package imported successfully")
|
136
193
|
except ImportError:
|
194
|
+
logger.error("❌ OpenAI package not installed")
|
137
195
|
raise IntelligenceError("OpenAI package not installed. Run: pip install openai")
|
138
196
|
|
139
197
|
model = config.get("model", "gpt-4")
|
@@ -143,19 +201,25 @@ def _invoke_azure_openai(prompt: str, config: Dict[str, Any]) -> str:
|
|
143
201
|
temperature = config.get("temperature", 0.7)
|
144
202
|
max_tokens = config.get("max_tokens", 150)
|
145
203
|
|
204
|
+
logger.debug(f"🔧 Azure config: model={model}, endpoint={endpoint}, api_version={api_version}, temp={temperature}, max_tokens={max_tokens}")
|
205
|
+
|
146
206
|
if not api_key:
|
207
|
+
logger.error("❌ Azure OpenAI API key not found")
|
147
208
|
raise ConfigurationError("Azure OpenAI API key not found in config or AZURE_OPENAI_API_KEY environment variable")
|
148
209
|
|
149
210
|
if not endpoint:
|
211
|
+
logger.error("❌ Azure OpenAI endpoint not found")
|
150
212
|
raise ConfigurationError("Azure OpenAI endpoint not found in config or AZURE_OPENAI_ENDPOINT environment variable")
|
151
213
|
|
152
214
|
try:
|
215
|
+
logger.debug("🔗 Creating Azure OpenAI client")
|
153
216
|
client = openai.AzureOpenAI(
|
154
217
|
api_key=api_key,
|
155
218
|
azure_endpoint=endpoint,
|
156
219
|
api_version=api_version
|
157
220
|
)
|
158
221
|
|
222
|
+
logger.debug("📡 Sending request to Azure OpenAI API")
|
159
223
|
response = client.chat.completions.create(
|
160
224
|
model=model,
|
161
225
|
messages=[{"role": "user", "content": prompt}],
|
@@ -165,11 +229,14 @@ def _invoke_azure_openai(prompt: str, config: Dict[str, Any]) -> str:
|
|
165
229
|
|
166
230
|
content = response.choices[0].message.content
|
167
231
|
if content is None:
|
232
|
+
logger.error("❌ Azure OpenAI returned empty response")
|
168
233
|
raise IntelligenceError("Azure OpenAI returned empty response")
|
234
|
+
|
235
|
+
logger.debug(f"✅ Azure OpenAI API call successful")
|
169
236
|
return content
|
170
237
|
|
171
238
|
except Exception as e:
|
172
|
-
|
239
|
+
logger.error(f"❌ Azure OpenAI API error: {type(e).__name__}: {e}")
|
173
240
|
raise IntelligenceError(f"Azure OpenAI API error: {e}")
|
174
241
|
|
175
242
|
|
@@ -182,9 +249,13 @@ def _invoke_local(prompt: str, config: Dict[str, Any]) -> str:
|
|
182
249
|
temperature = config.get("temperature", 0.7)
|
183
250
|
max_tokens = config.get("max_tokens", 150)
|
184
251
|
|
252
|
+
logger.debug(f"🟢 Initializing local provider")
|
253
|
+
logger.debug(f"🔧 Local config: model={model}, endpoint={endpoint}, temp={temperature}, max_tokens={max_tokens}")
|
254
|
+
|
185
255
|
try:
|
186
256
|
# Format for Ollama API
|
187
257
|
if "ollama" in endpoint or ":11434" in endpoint:
|
258
|
+
logger.debug("📦 Using Ollama API format")
|
188
259
|
payload = {
|
189
260
|
"model": model,
|
190
261
|
"prompt": prompt,
|
@@ -195,7 +266,7 @@ def _invoke_local(prompt: str, config: Dict[str, Any]) -> str:
|
|
195
266
|
}
|
196
267
|
}
|
197
268
|
else:
|
198
|
-
|
269
|
+
logger.debug("📦 Using generic local API format")
|
199
270
|
payload = {
|
200
271
|
"model": model,
|
201
272
|
"prompt": prompt,
|
@@ -203,6 +274,7 @@ def _invoke_local(prompt: str, config: Dict[str, Any]) -> str:
|
|
203
274
|
"max_tokens": max_tokens
|
204
275
|
}
|
205
276
|
|
277
|
+
logger.debug(f"📡 Sending request to local endpoint: {endpoint}")
|
206
278
|
response = requests.post(endpoint, json=payload, timeout=30)
|
207
279
|
response.raise_for_status()
|
208
280
|
|
@@ -210,19 +282,25 @@ def _invoke_local(prompt: str, config: Dict[str, Any]) -> str:
|
|
210
282
|
|
211
283
|
# Handle different response formats
|
212
284
|
if "response" in result:
|
213
|
-
|
285
|
+
response_text = result["response"] # Ollama format
|
286
|
+
logger.debug("✅ Local API call successful (Ollama format)")
|
214
287
|
elif "text" in result:
|
215
|
-
|
288
|
+
response_text = result["text"] # Generic format
|
289
|
+
logger.debug("✅ Local API call successful (generic format)")
|
216
290
|
elif "choices" in result and len(result["choices"]) > 0:
|
217
|
-
|
291
|
+
response_text = result["choices"][0].get("text", "") # OpenAI-compatible format
|
292
|
+
logger.debug("✅ Local API call successful (OpenAI-compatible format)")
|
218
293
|
else:
|
294
|
+
logger.error(f"❌ Unexpected response format from local provider: {result}")
|
219
295
|
raise IntelligenceError(f"Unexpected response format from local provider: {result}")
|
296
|
+
|
297
|
+
return response_text
|
220
298
|
|
221
299
|
except requests.RequestException as e:
|
222
|
-
|
300
|
+
logger.error(f"❌ Local provider request error: {type(e).__name__}: {e}")
|
223
301
|
raise IntelligenceError(f"Local provider request error: {e}")
|
224
302
|
except Exception as e:
|
225
|
-
|
303
|
+
logger.error(f"❌ Local provider error: {type(e).__name__}: {e}")
|
226
304
|
raise IntelligenceError(f"Local provider error: {e}")
|
227
305
|
|
228
306
|
|
@@ -244,29 +322,57 @@ def validate_config(config: Dict[str, Any]) -> bool:
|
|
244
322
|
Raises:
|
245
323
|
ConfigurationError: If configuration is invalid
|
246
324
|
"""
|
325
|
+
logger.debug(f"🔍 Validating intelligence configuration")
|
326
|
+
|
247
327
|
if not isinstance(config, dict):
|
328
|
+
logger.error("❌ Configuration must be a dictionary")
|
248
329
|
raise ConfigurationError("Configuration must be a dictionary")
|
249
330
|
|
250
331
|
engine = config.get("engine")
|
251
332
|
if not engine:
|
333
|
+
logger.error("❌ Missing 'engine' in configuration")
|
252
334
|
raise ConfigurationError("Missing 'engine' in configuration")
|
253
335
|
|
254
336
|
if engine.lower() not in get_supported_engines():
|
337
|
+
logger.error(f"❌ Unsupported engine: {engine}")
|
255
338
|
raise ConfigurationError(f"Unsupported engine: {engine}. Supported engines: {get_supported_engines()}")
|
256
339
|
|
257
340
|
# Engine-specific validation
|
258
341
|
engine = engine.lower()
|
342
|
+
logger.debug(f"🔧 Validating {engine} specific configuration")
|
259
343
|
|
260
344
|
if engine in ["openai", "azure"]:
|
261
345
|
if not config.get("api_key") and not os.getenv("OPENAI_API_KEY") and not os.getenv("AZURE_OPENAI_API_KEY"):
|
346
|
+
logger.error(f"❌ API key required for {engine} engine")
|
262
347
|
raise ConfigurationError(f"API key required for {engine} engine")
|
263
348
|
|
264
349
|
elif engine == "anthropic":
|
265
350
|
if not config.get("api_key") and not os.getenv("ANTHROPIC_API_KEY"):
|
351
|
+
logger.error("❌ API key required for Anthropic engine")
|
266
352
|
raise ConfigurationError("API key required for Anthropic engine")
|
267
353
|
|
268
354
|
elif engine == "local":
|
269
355
|
if not config.get("endpoint"):
|
270
356
|
config["endpoint"] = "http://localhost:11434/api/generate" # Default to Ollama
|
357
|
+
logger.debug("🔧 Set default endpoint for local engine")
|
358
|
+
|
359
|
+
logger.debug(f"✅ Configuration validation successful for {engine}")
|
360
|
+
return True
|
361
|
+
|
362
|
+
|
363
|
+
def _sanitize_config_for_logging(config: Dict[str, Any]) -> Dict[str, Any]:
|
364
|
+
"""Sanitize config for logging by masking sensitive data."""
|
365
|
+
sanitized = config.copy()
|
366
|
+
|
367
|
+
# Mask sensitive fields
|
368
|
+
sensitive_fields = ['api_key', 'password', 'token', 'secret']
|
369
|
+
for field in sensitive_fields:
|
370
|
+
if field in sanitized and sanitized[field]:
|
371
|
+
# Show first 4 and last 4 characters, mask the rest
|
372
|
+
value = str(sanitized[field])
|
373
|
+
if len(value) > 8:
|
374
|
+
sanitized[field] = f"{value[:4]}...{value[-4:]}"
|
375
|
+
else:
|
376
|
+
sanitized[field] = "***"
|
271
377
|
|
272
|
-
return
|
378
|
+
return sanitized
|