Claude 24f816b6a3
Consolidate 22 sibling repos into layered organism structure
Place useful parts of the surrounding repos into sica-fondt by layer, per the
body model (Ada = membrane; brain/endocrine/capabilities/knowledge non-Ada):

- brain/        LLM reasoning + providers (dapr, hermes, MoMoA)
- capabilities/ REPRAG sidecars: hermes tools/skills, dapr tools, parallel
                dispatch, A51 channels, and the OSINT cluster
- knowledge/    LORAG corpus: 754 cyber-skills, agency personas, secure-coding,
                MITRE ATT&CK data
- reference/    defensive threat-reference (C3, shhbruh doc) + AdaYaml parser

License handling: AGPL sources (worldosint, advanced_evolution, mercury,
Reticulum) and GPL DeTTECT are SPEC-only clean-room/port descriptions — no
copyleft code copied. MIT/Apache/data parts copied as working trees.

Safety: shhbruh escape/persistence material and C3 covert-C2 kept as reference
only, not wired into the running organism. See CONSOLIDATION.md.

https://claude.ai/code/session_01UehUqEXXJJCsHoA4voCU5c
2026-06-10 06:53:01 +00:00

217 lines
8.5 KiB
Python

#
# Copyright 2026 The Dapr Authors
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from dapr_agents.types.llm import DaprInferenceClientConfig
from dapr_agents.llm.base import LLMClientBase
from dapr.clients import DaprClient
from dapr.clients.grpc import conversation as dapr_conversation
from typing import Dict, Any, List, Optional
from pydantic import model_validator
from google.protobuf import json_format
from google.protobuf.struct_pb2 import Struct as GrpcStruct
import json
import logging
logger = logging.getLogger(__name__)
class DaprInferenceClient:
def __init__(self):
pass # No persistent client - use per-call context manager
def get_metadata(self):
"""Fetch Dapr sidecar metadata using a fresh per-call client."""
with DaprClient() as client:
return client.get_metadata()
# ──────────────────────────────────────────────────────────────────────────
# Alpha2 (Tool Calling) support
# ──────────────────────────────────────────────────────────────────────────
def _convert_openai_tools_to_conversation_tools(
self, tools: Optional[List[Dict[str, Any]]]
) -> Optional[List[dapr_conversation.ConversationTools]]:
"""
Convert OpenAI-style tools (type=function, function={name, description, parameters})
into Dapr ConversationTools objects for Alpha2.
"""
if not tools:
return None
converted: List[dapr_conversation.ConversationTools] = []
for tool in tools:
fn = tool.get("function", {}) if isinstance(tool, dict) else {}
name = fn.get("name")
description = fn.get("description")
parameters = fn.get("parameters")
function_spec = dapr_conversation.ConversationToolsFunction(
name=name or "",
description=description or "",
parameters=parameters or {},
)
conv_tool = dapr_conversation.ConversationTools(function=function_spec)
converted.append(conv_tool)
return converted
def chat_completion_alpha2(
self,
*,
llm: str,
inputs: List[dapr_conversation.ConversationInputAlpha2],
scrub_pii: Optional[bool] = None,
temperature: Optional[float] = None,
tools: Optional[List[Dict[str, Any]]] = None,
tool_choice: Optional[str] = None,
context_id: Optional[str] = None,
parameters: Optional[Dict[str, Any]] = None,
response_format: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
"""
Invoke Dapr Conversation API Alpha2 with optional tool-calling support and
convert the response into a simplified OpenAI-like JSON envelope.
"""
conv_tools = self._convert_openai_tools_to_conversation_tools(tools)
# TODO: Remove when langchaningo is updated in contrib to latest version with a fix for openai-like temperature
if temperature is None:
temperature = 1
with DaprClient() as client:
kwargs: Dict[str, Any] = dict(
name=llm,
inputs=inputs,
context_id=context_id,
parameters=parameters,
scrub_pii=scrub_pii,
temperature=temperature,
tools=conv_tools,
tool_choice=tool_choice,
)
if response_format is not None:
kwargs["response_format"] = json_format.ParseDict(
response_format, GrpcStruct()
)
response_alpha2 = client.converse_alpha2(**kwargs)
outputs: List[Dict[str, Any]] = []
for output in getattr(response_alpha2, "outputs", []) or []:
choices_list: List[Dict[str, Any]] = []
for choice in getattr(output, "choices", []) or []:
msg = getattr(choice, "message", None)
content = getattr(msg, "content", None) if msg else None
# Convert tool calls if present
tool_calls_json: Optional[List[Dict[str, Any]]] = None
if msg and getattr(msg, "tool_calls", None):
tool_calls_json = []
for tc in msg.tool_calls:
fn = getattr(tc, "function", None)
arguments = getattr(fn, "arguments", None) if fn else None
if isinstance(arguments, (dict, list)):
try:
arguments = json.dumps(arguments)
except Exception:
arguments = str(arguments)
elif arguments is None:
arguments = ""
tool_calls_json.append(
{
"id": getattr(tc, "id", ""),
"type": "function",
"function": {
"name": getattr(fn, "name", "") if fn else "",
"arguments": arguments,
},
}
)
choices_list.append(
{
"message": {
"role": "assistant",
"content": content,
**(
{"tool_calls": tool_calls_json}
if tool_calls_json
else {}
),
},
"finish_reason": getattr(choice, "finish_reason", "stop"),
}
)
outputs.append({"choices": choices_list})
return {
"context_id": getattr(response_alpha2, "context_id", None),
"outputs": outputs,
}
class DaprInferenceClientBase(LLMClientBase):
"""
Base class for managing Dapr Inference API clients.
Handles client initialization, configuration, and shared logic.
"""
@model_validator(mode="before")
def validate_and_initialize(cls, values: Dict[str, Any]) -> Dict[str, Any]:
return values
def model_post_init(self, __context: Any) -> None:
"""
Initializes private attributes after validation.
"""
self._provider = "dapr"
# Set up the private config and client attributes
self._config = self.get_config()
self._client = self.get_client()
return super().model_post_init(__context)
def get_config(self) -> DaprInferenceClientConfig:
"""
Returns the appropriate configuration for the Dapr Conversation API.
"""
return DaprInferenceClientConfig()
def get_client(self) -> DaprInferenceClient:
"""
Initializes and returns the Dapr Inference client.
"""
return DaprInferenceClient()
@classmethod
def from_config(
cls, client_options: DaprInferenceClientConfig, timeout: float = 1500
):
"""
Initializes the DaprInferenceClientBase using DaprInferenceClientConfig.
Args:
client_options: The configuration options for the client.
timeout: Timeout for requests (default is 1500 seconds).
Returns:
DaprInferenceClientBase: The initialized client instance.
"""
return cls()
@property
def config(self) -> Dict[str, Any]:
return self._config
@property
def client(self) -> DaprInferenceClient:
return self._client