JerameeUC
1st
732e77c
# /agenticcore/chatbot/services.py
from __future__ import annotations
import json
import os
from dataclasses import dataclass
from typing import Dict
# Delegate sentiment to the unified provider layer
# If you put providers_unified.py under agenticcore/chatbot/, change the import to:
# from agenticcore.chatbot.providers_unified import analyze_sentiment
from agenticcore.providers_unified import analyze_sentiment
from ..providers_unified import analyze_sentiment
def _trim(s: str, max_len: int = 2000) -> str:
s = (s or "").strip()
return s if len(s) <= max_len else s[: max_len - 1] + "…"
@dataclass(frozen=True)
class SentimentResult:
label: str # "positive" | "neutral" | "negative" | "mixed" | "unknown"
confidence: float # 0.0 .. 1.0
class ChatBot:
"""
Minimal chatbot that uses provider-agnostic sentiment via providers_unified.
Public API:
- reply(text: str) -> Dict[str, object]
- capabilities() -> Dict[str, object]
"""
def __init__(self, system_prompt: str = "You are a concise helper.") -> None:
self._system_prompt = _trim(system_prompt, 800)
# Expose which provider is intended/active (for diagnostics)
self._mode = os.getenv("AI_PROVIDER") or "auto"
def capabilities(self) -> Dict[str, object]:
"""List what this bot can do."""
return {
"system": "chatbot",
"mode": self._mode, # "auto" or a pinned provider (hf/azure/openai/cohere/deepai/offline)
"features": ["text-input", "sentiment-analysis", "help"],
"commands": {"help": "Describe capabilities and usage."},
}
def reply(self, text: str) -> Dict[str, object]:
"""Produce a reply and sentiment for one user message."""
user = _trim(text)
if not user:
return self._make_response(
"I didn't catch that. Please provide some text.",
SentimentResult("unknown", 0.0),
)
if user.lower() in {"help", "/help"}:
return {"reply": self._format_help(), "capabilities": self.capabilities()}
s = analyze_sentiment(user) # -> {"provider", "label", "score", ...}
sr = SentimentResult(label=str(s.get("label", "neutral")), confidence=float(s.get("score", 0.5)))
return self._make_response(self._compose(sr), sr)
# ---- internals ----
def _format_help(self) -> str:
caps = self.capabilities()
feats = ", ".join(caps["features"])
return f"I can analyze sentiment and respond concisely. Features: {feats}. Send any text or type 'help'."
@staticmethod
def _make_response(reply: str, s: SentimentResult) -> Dict[str, object]:
return {"reply": reply, "sentiment": s.label, "confidence": round(float(s.confidence), 2)}
@staticmethod
def _compose(s: SentimentResult) -> str:
if s.label == "positive":
return "Thanks for sharing. I detected a positive sentiment."
if s.label == "negative":
return "I hear your concern. I detected a negative sentiment."
if s.label == "neutral":
return "Noted. The sentiment appears neutral."
if s.label == "mixed":
return "Your message has mixed signals. Can you clarify?"
return "I could not determine the sentiment. Please rephrase."
# Optional: local REPL for quick manual testing
def _interactive_loop() -> None:
bot = ChatBot()
try:
while True:
msg = input("> ").strip()
if msg.lower() in {"exit", "quit"}:
break
print(json.dumps(bot.reply(msg), ensure_ascii=False))
except (EOFError, KeyboardInterrupt):
pass
if __name__ == "__main__":
_interactive_loop()