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

161 lines
5.7 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.
#
import logging
from typing import Any, Dict, Optional, Union
from dapr_agents.llm.openai.client.base import OpenAIClientBase
from dapr_agents.llm.utils import RequestHandler
from dapr_agents.types.llm import (
AudioSpeechRequest,
AudioTranscriptionRequest,
AudioTranscriptionResponse,
AudioTranslationRequest,
AudioTranslationResponse,
)
logger = logging.getLogger(__name__)
class OpenAIAudioClient(OpenAIClientBase):
"""
Client for handling OpenAI's audio functionalities, including speech generation, transcription, and translation.
Inherits shared logic and configuration from OpenAIClientBase.
"""
def model_post_init(self, __context: Any) -> None:
"""
Initializes the private attributes specific to the audio client.
"""
self._api = "audio"
super().model_post_init(__context)
def create_speech(
self,
request: Union[AudioSpeechRequest, Dict[str, Any]],
file_name: Optional[str] = None,
) -> Union[bytes, None]:
"""
Generate speech audio from text and optionally save it to a file.
Args:
request (Union[AudioSpeechRequest, Dict[str, Any]]): The request parameters for speech generation.
file_name (Optional[str]): Optional file name to save the generated audio.
Returns:
Union[bytes, None]: The generated audio content as bytes if no file_name is provided, otherwise None.
"""
# Transform dictionary to Pydantic object if needed
validated_request: AudioSpeechRequest = RequestHandler.validate_request(
request, AudioSpeechRequest
)
logger.info(f"Using model '{validated_request.model}' for speech generation.")
input_text = validated_request.input
max_chunk_size = 4096
if len(input_text) > max_chunk_size:
logger.info(
f"Input exceeds {max_chunk_size} characters. Splitting into smaller chunks."
)
# Split input text into manageable chunks
def split_text(text, max_size):
chunks = []
while len(text) > max_size:
split_index = text.rfind(". ", 0, max_size) + 1 or max_size
chunks.append(text[:split_index].strip())
text = text[split_index:].strip()
chunks.append(text)
return chunks
text_chunks = split_text(input_text, max_chunk_size)
audio_chunks = []
try:
for chunk in text_chunks:
validated_request.input = chunk
with self.client.with_streaming_response.audio.speech.create(
**validated_request.model_dump()
) as response:
if file_name:
# Write each chunk incrementally to the file
logger.info(f"Saving audio chunk to file: {file_name}")
with open(file_name, "ab") as audio_file:
for chunk in response.iter_bytes():
audio_file.write(chunk)
else:
# Collect all chunks in memory for combining
audio_chunks.extend(response.iter_bytes())
if file_name:
return None
else:
# Combine all chunks into one bytes object
return b"".join(audio_chunks)
except Exception as e:
logger.error(f"Failed to create or save speech: {e}")
raise ValueError(f"An error occurred during speech generation: {e}")
def create_transcription(
self, request: Union[AudioTranscriptionRequest, Dict[str, Any]]
) -> AudioTranscriptionResponse:
"""
Transcribe audio to text.
Args:
request (Union[AudioTranscriptionRequest, Dict[str, Any]]): The request parameters for transcription.
Returns:
AudioTranscriptionResponse: The transcription result.
"""
validated_request: AudioTranscriptionRequest = RequestHandler.validate_request(
request, AudioTranscriptionRequest
)
logger.info(f"Using model '{validated_request.model}' for transcription.")
response = self.client.audio.transcriptions.create(
file=validated_request.file,
**validated_request.model_dump(exclude={"file"}),
)
return response
def create_translation(
self, request: Union[AudioTranslationRequest, Dict[str, Any]]
) -> AudioTranslationResponse:
"""
Translate audio to English.
Args:
request (Union[AudioTranslationRequest, Dict[str, Any]]): The request parameters for translation.
Returns:
AudioTranslationResponse: The translation result.
"""
validated_request: AudioTranslationRequest = RequestHandler.validate_request(
request, AudioTranslationRequest
)
logger.info(f"Using model '{validated_request.model}' for translation.")
response = self.client.audio.translations.create(
file=validated_request.file,
**validated_request.model_dump(exclude={"file"}),
)
return response