Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Setup
- Install CogSol Framework from: https://github.com/Pyxis-Cognitive-Solutions/cogsol-framework
- Install local dependencies: `pip install -r requirements.txt`
- Configure environment variables in `.env` (see `.env.example`).
- Migrate data app first: `python manage.py migrate data`.
- Migrate agents app next: `python manage.py migrate`.
Expand All @@ -24,6 +25,10 @@
## Running the Agent
- Start chat with the agent: `python manage.py chat --agent CogsolFrameworkAgent`.

## Language Behavior
- The assistant detects and pins response language from the first user message in each chat.
- Responses stay in that language unless the user explicitly asks to switch language.

## MCP Server
- Install MCP support: `python -m pip install mcp`.
- Run the server over stdio: `python mcp_server.py`.
Expand Down
3 changes: 2 additions & 1 deletion agents/cogsolframeworkagent/agent.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from cogsol.agents import BaseAgent, genconfigs
from cogsol.prompts import Prompts
from ..searches import CogsolFrameworkDocsSearch, CogsolAPIsDocsSearch
from ..tools import CogSolScaffoldGenerator
from ..tools import CogSolScaffoldGenerator, DetectUserLanguageTool


class CogsolFrameworkAgent(BaseAgent):
system_prompt = Prompts.load("cogsolframeworkagent.md")
generation_config = genconfigs.QA()
pretools = [DetectUserLanguageTool()]
tools = [CogsolFrameworkDocsSearch(), CogsolAPIsDocsSearch(), CogSolScaffoldGenerator()]
max_responses = 20
max_msg_length = 2048
Expand Down
4 changes: 4 additions & 0 deletions agents/cogsolframeworkagent/prompts/cogsolframeworkagent.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ When developers ask questions:

## Response Guidelines

- Respect language context injected at runtime:
- If `Preferred response language` is present, answer in that language.
- Keep the same language across the session unless the user explicitly asks to switch language.
- If language context is missing, default to English.
- Provide code examples with correct imports and file paths
- Reference the appropriate files (e.g., `agents/tools.py`, `data/retrievals.py`)
- Explain configuration options and their effects
Expand Down
157 changes: 157 additions & 0 deletions agents/tools.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,162 @@
from cogsol.tools import BaseTool, tool_params


class DetectUserLanguageTool(BaseTool):
"""Detect and enforce the response language from chat history."""

name = "detect_user_language"
description = (
"Detect the user's language from chat messages and inject language instructions "
"into prompt context before the assistant generates a response."
)

def run(self, chat=None, data=None, secrets=None, log=None):
if data is None:
data = {}

prompt_params = data.setdefault("prompt_params", {})
context = prompt_params.setdefault("context", {})

first_user_message, last_user_message = self._get_user_messages(chat)

first_lang_name, first_lang_code, first_reliable = self._detect_language(
first_user_message
)
last_lang_name, last_lang_code, last_reliable = self._detect_language(
last_user_message
)

if first_reliable:
selected_name, selected_code = first_lang_name, first_lang_code
policy_source = "first_user_message"
elif last_reliable:
selected_name, selected_code = last_lang_name, last_lang_code
policy_source = "last_user_message_fallback"
else:
selected_name, selected_code = "English", "en"
policy_source = "default_english"

no_info_source = self._get_no_info_message(chat)
translated_no_info = self._translate_no_info_message(
no_info_source=no_info_source,
target_code=selected_code,
)

context["Preferred response language"] = selected_name
context["Response language policy"] = (
"Always respond in the language of the first user message unless the user explicitly asks to switch language."
)
context["Message of not having information"] = translated_no_info

if log:
log(
"DetectUserLanguageTool => selected="
f"{selected_name} ({selected_code}), source={policy_source}, "
f"first_reliable={first_reliable}, last_reliable={last_reliable}"
)

return {
"selected_language": selected_name,
"selected_language_code": selected_code,
"policy_source": policy_source,
"first_message_reliable": first_reliable,
"last_message_reliable": last_reliable,
}

def _get_user_messages(self, chat):
"""Return first and last user messages from chat history."""
empty = ""
if chat is None or not hasattr(chat, "messages"):
return empty, empty

try:
user_messages = chat.messages.filter(role="user").order_by("msg_num")

first_obj = (
user_messages.first() if hasattr(user_messages, "first") else None
)
last_obj = user_messages.last() if hasattr(user_messages, "last") else None

first_text = getattr(first_obj, "content", "") if first_obj else ""
last_text = getattr(last_obj, "content", "") if last_obj else ""
return first_text or "", last_text or ""
except Exception:
return empty, empty

def _get_no_info_message(self, chat):
"""Get the current no-information message from assistant config."""
default_message = "I don't have information on that topic."

try:
assistant = getattr(chat, "assistant", None)
message = getattr(assistant, "not_info_message", None)
return message or default_message
except Exception:
return default_message

def _detect_language(self, text):
"""Detect language from text with pycld2, returning safe defaults."""
if not text or not text.strip():
return "English", "en", False

try:
import pycld2 as cld2

is_reliable, _text_bytes_found, details = cld2.detect(text)
if not details:
return "English", "en", False

lang_name = details[0][0] or "English"
lang_code = self._normalize_lang_code(details[0][1])
return lang_name, lang_code, bool(is_reliable)
except Exception:
return "English", "en", False

def _normalize_lang_code(self, code):
"""Normalize detected language codes into translator-safe two-letter codes."""
if not code:
return "en"

normalized = str(code).strip().lower().replace("_", "-")

aliases = {
"zh-hans": "zh",
"zh-hant": "zh",
"pt-br": "pt",
"pt-pt": "pt",
}

if normalized in aliases:
return aliases[normalized]

if "-" in normalized:
normalized = normalized.split("-", 1)[0]

return normalized[:2] if normalized else "en"

def _translate_no_info_message(self, no_info_source, target_code):
"""Translate no-information message to target language with safe fallback."""
if not no_info_source:
return "I don't have information on that topic."

if not target_code or target_code == "en":
return no_info_source

source_lang_name, source_lang_code, source_reliable = self._detect_language(
no_info_source
)
source_code = source_lang_code if source_reliable else "en"

try:
from translate import Translator

translator = Translator(to_lang=target_code, from_lang=source_code)
translated = translator.translate(no_info_source)
return translated or no_info_source
except Exception:
return no_info_source


class CogSolScaffoldGenerator(BaseTool):
"""Generate boilerplate code for CogSol Framework components."""

Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pycld2>=0.42
translate>=3.6.1