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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*__pycache__
8 changes: 7 additions & 1 deletion llm_model/llm_model/chatgpt.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
import time
import openai
from llm_config.user_config import UserConfig
from llm_model.service_response_text import (
normalize_function_call_response_text,
)


# Global Initialization
Expand Down Expand Up @@ -274,16 +277,19 @@ def function_call_response_callback(self, future):
the function_call_response_callback will call the gpt service again
to get the text response to user
"""
response = None
err = None
try:
response = future.result()
self.get_logger().info(
f"Response from ChatGPT_function_call_service: {response}"
)

except Exception as e:
err = e
self.get_logger().info(f"ChatGPT function call service failed {e}")

response_text = "null"
response_text = normalize_function_call_response_text(response, error=err)
self.add_message_to_history(
role="function",
name=self.function_name,
Expand Down
46 changes: 46 additions & 0 deletions llm_model/llm_model/service_response_text.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
"""Normalize ChatGPT function-call service responses for chat history."""

from __future__ import annotations

from typing import Any, Optional

_MAX_LEN = 4000


def normalize_function_call_response_text(
response: Any = None, error: Optional[BaseException] = None
) -> str:
"""Return a short string safe to store in OpenAI chat history.

On RPC failure, prefer an explicit error marker. On success, use
``response.response_text`` when present. Empty/missing becomes ``\"null\"``.
"""
if error is not None:
msg = str(error).strip() or type(error).__name__
if len(msg) > 500:
msg = msg[:500]
return f"error: {msg}"

if response is None:
return "null"

text = None
if isinstance(response, dict):
text = response.get("response_text")
else:
text = getattr(response, "response_text", None)

if text is None:
return "null"
if not isinstance(text, str):
if isinstance(text, bytes):
text = text.decode("utf-8", errors="replace")
else:
text = str(text)
text = text.strip()
if not text:
return "null"
if len(text) > _MAX_LEN:
return text[:_MAX_LEN]
return text
49 changes: 49 additions & 0 deletions llm_model/test/test_service_response_text.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
import os
import sys
import unittest

_HERE = os.path.dirname(os.path.abspath(__file__))
_PKG = os.path.normpath(os.path.join(_HERE, "..", "llm_model"))
if _PKG not in sys.path:
sys.path.insert(0, _PKG)

from service_response_text import normalize_function_call_response_text # noqa: E402


class _Resp:
def __init__(self, response_text):
self.response_text = response_text


class TestServiceResponseText(unittest.TestCase):
def test_success_passthrough(self):
self.assertEqual(
normalize_function_call_response_text(_Resp("moved 1m")),
"moved 1m",
)

def test_dict_response(self):
self.assertEqual(
normalize_function_call_response_text({"response_text": "ok"}),
"ok",
)

def test_empty_and_none(self):
self.assertEqual(normalize_function_call_response_text(None), "null")
self.assertEqual(normalize_function_call_response_text(_Resp("")), "null")
self.assertEqual(normalize_function_call_response_text(_Resp(None)), "null")

def test_error_path(self):
out = normalize_function_call_response_text(error=RuntimeError("boom"))
self.assertTrue(out.startswith("error: "))
self.assertIn("boom", out)

def test_bounds(self):
long = "x" * 5000
out = normalize_function_call_response_text(_Resp(long))
self.assertEqual(len(out), 4000)


if __name__ == "__main__":
unittest.main()