Skip to content

Commit 0c7de14

Browse files
authored
Merge pull request lightspeed-core#208 from tisnik/lcore-293-use-customized-system-prompt
LCORE-293: use customized system prompt if provided
2 parents b2f72b2 + fec6903 commit 0c7de14

3 files changed

Lines changed: 198 additions & 6 deletions

File tree

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ Lightspeed Core Stack (LCS) is an AI powered assistant that provides answers to
1414
* [Prerequisities](#prerequisities)
1515
* [Installation](#installation)
1616
* [Configuration](#configuration)
17+
* [Llama Stack as separate server](#llama-stack-as-separate-server)
18+
* [Llama Stack as client library](#llama-stack-as-client-library)
19+
* [System prompt](#system-prompt)
1720
* [Usage](#usage)
1821
* [Make targets](#make-targets)
1922
* [Running Linux container image](#running-linux-container-image)
@@ -100,6 +103,17 @@ user_data_collection:
100103
transcripts_storage: "/tmp/data/transcripts"
101104
```
102105
106+
## System prompt
107+
108+
The service uses the, so called, system prompt to put the question into context before the question is sent to the selected LLM. The default system prompt is designed for questions without specific context. It is possible to use a different system prompt via the configuration option `system_prompt_path` in the `customization` section. That option must contain the path to the text file with the actual system prompt (can contain multiple lines). An example of such configuration:
109+
110+
```yaml
111+
customization:
112+
system_prompt_path: "system_prompts/system_prompt_for_product_XYZZY"
113+
```
114+
115+
Additionally an optional string parameter `system_prompt` can be specified in `/v1/query` and `/v1/streaming_query` endpoints to override the configured system prompt.
116+
103117

104118

105119
# Usage

src/utils/endpoints.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,19 @@ def check_configuration_loaded(configuration: AppConfig) -> None:
1616
)
1717

1818

19-
def get_system_prompt(query_request: QueryRequest, _configuration: AppConfig) -> str:
19+
def get_system_prompt(query_request: QueryRequest, configuration: AppConfig) -> str:
2020
"""Get the system prompt: the provided one, configured one, or default one."""
21-
return (
22-
query_request.system_prompt
23-
if query_request.system_prompt
24-
else constants.DEFAULT_SYSTEM_PROMPT
25-
)
21+
# system prompt defined in query request has precendence
22+
if query_request.system_prompt:
23+
return query_request.system_prompt
24+
25+
# customized system prompt should be used when query request
26+
# does not contain one
27+
if (
28+
configuration.customization is not None
29+
and configuration.customization.system_prompt is not None
30+
):
31+
return configuration.customization.system_prompt
32+
33+
# default system prompt has the lowest precedence
34+
return constants.DEFAULT_SYSTEM_PROMPT

tests/unit/utils/test_endpoints.py

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
"""Unit tests for endpoints utility functions."""
2+
3+
import os
4+
import pytest
5+
6+
import constants
7+
from configuration import AppConfig
8+
9+
from models.requests import QueryRequest
10+
from utils import endpoints
11+
12+
13+
@pytest.fixture
14+
def input_file(tmp_path):
15+
"""Create file manually using the tmp_path fixture."""
16+
filename = os.path.join(tmp_path, "prompt.txt")
17+
with open(filename, "wt") as fout:
18+
fout.write("this is prompt!")
19+
return filename
20+
21+
22+
def test_get_default_system_prompt():
23+
"""Test that default system prompt is returned when other prompts are not provided."""
24+
config_dict = {
25+
"name": "foo",
26+
"service": {
27+
"host": "localhost",
28+
"port": 8080,
29+
"auth_enabled": False,
30+
"workers": 1,
31+
"color_log": True,
32+
"access_log": True,
33+
},
34+
"llama_stack": {
35+
"api_key": "xyzzy",
36+
"url": "http://x.y.com:1234",
37+
"use_as_library_client": False,
38+
},
39+
"user_data_collection": {
40+
"feedback_disabled": True,
41+
},
42+
"mcp_servers": [],
43+
"customization": None,
44+
}
45+
46+
# no customization provided
47+
cfg = AppConfig()
48+
cfg.init_from_dict(config_dict)
49+
50+
# no system prompt in query request
51+
query_request = QueryRequest(query="query", system_prompt=None)
52+
53+
# default system prompt needs to be returned
54+
system_prompt = endpoints.get_system_prompt(query_request, cfg)
55+
assert system_prompt == constants.DEFAULT_SYSTEM_PROMPT
56+
57+
58+
def test_get_customized_system_prompt():
59+
"""Test that customized system prompt is used when system prompt is not provided in query."""
60+
config_dict = {
61+
"name": "foo",
62+
"service": {
63+
"host": "localhost",
64+
"port": 8080,
65+
"auth_enabled": False,
66+
"workers": 1,
67+
"color_log": True,
68+
"access_log": True,
69+
},
70+
"llama_stack": {
71+
"api_key": "xyzzy",
72+
"url": "http://x.y.com:1234",
73+
"use_as_library_client": False,
74+
},
75+
"user_data_collection": {
76+
"feedback_disabled": True,
77+
},
78+
"mcp_servers": [],
79+
"customization": {
80+
"system_prompt": "This is system prompt",
81+
},
82+
}
83+
84+
# no customization provided
85+
cfg = AppConfig()
86+
cfg.init_from_dict(config_dict)
87+
88+
# no system prompt in query request
89+
query_request = QueryRequest(query="query", system_prompt=None)
90+
91+
# default system prompt needs to be returned
92+
system_prompt = endpoints.get_system_prompt(query_request, cfg)
93+
assert system_prompt == "This is system prompt"
94+
95+
96+
def test_get_query_system_prompt():
97+
"""Test that system prompt from query is returned."""
98+
config_dict = {
99+
"name": "foo",
100+
"service": {
101+
"host": "localhost",
102+
"port": 8080,
103+
"auth_enabled": False,
104+
"workers": 1,
105+
"color_log": True,
106+
"access_log": True,
107+
},
108+
"llama_stack": {
109+
"api_key": "xyzzy",
110+
"url": "http://x.y.com:1234",
111+
"use_as_library_client": False,
112+
},
113+
"user_data_collection": {
114+
"feedback_disabled": True,
115+
},
116+
"mcp_servers": [],
117+
"customization": None,
118+
}
119+
120+
# no customization provided
121+
cfg = AppConfig()
122+
cfg.init_from_dict(config_dict)
123+
124+
# system prompt defined in query request
125+
system_prompt = "System prompt defined in query"
126+
query_request = QueryRequest(query="query", system_prompt=system_prompt)
127+
128+
# default system prompt needs to be returned
129+
system_prompt = endpoints.get_system_prompt(query_request, cfg)
130+
assert system_prompt == system_prompt
131+
132+
133+
def test_get_query_system_prompt_not_customized_one():
134+
"""Test that system prompt from query is returned even when customized one is specified."""
135+
config_dict = {
136+
"name": "foo",
137+
"service": {
138+
"host": "localhost",
139+
"port": 8080,
140+
"auth_enabled": False,
141+
"workers": 1,
142+
"color_log": True,
143+
"access_log": True,
144+
},
145+
"llama_stack": {
146+
"api_key": "xyzzy",
147+
"url": "http://x.y.com:1234",
148+
"use_as_library_client": False,
149+
},
150+
"user_data_collection": {
151+
"feedback_disabled": True,
152+
},
153+
"mcp_servers": [],
154+
"customization": {
155+
"system_prompt": "This is system prompt",
156+
},
157+
}
158+
159+
# no customization provided
160+
cfg = AppConfig()
161+
cfg.init_from_dict(config_dict)
162+
163+
# system prompt defined in query request
164+
system_prompt = "System prompt defined in query"
165+
query_request = QueryRequest(query="query", system_prompt=system_prompt)
166+
167+
# default system prompt needs to be returned
168+
system_prompt = endpoints.get_system_prompt(query_request, cfg)
169+
assert system_prompt == system_prompt

0 commit comments

Comments
 (0)