Skip to content

Commit

Permalink
Merge pull request #23 from ijwfly/feature/user-system-prompt-configu…
Browse files Browse the repository at this point in the history
…ration

User info in system prompt
  • Loading branch information
ijwfly committed Jan 14, 2024
2 parents bdb8ad8 + ef9e0d7 commit ee9f62f
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 22 deletions.
4 changes: 3 additions & 1 deletion app/bot/message_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ async def process(self, is_cancelled):
context_manager = await self.context_manager()

function_storage = await context_manager.get_function_storage()
chat_gpt_manager = ChatGptManager(ChatGPT(self.user.current_model, self.user.gpt_mode, function_storage), self.db)
system_prompt = await context_manager.get_system_prompt()
chat_gpt_manager = ChatGptManager(ChatGPT(self.user.current_model, system_prompt, function_storage), self.db)

context_dialog_messages = await context_manager.get_context_messages()
response_generator = await chat_gpt_manager.send_user_message(self.user, context_dialog_messages, is_cancelled)
Expand All @@ -85,6 +86,7 @@ async def handle_gpt_response(self, chat_gpt_manager, context_manager, response_

response_dialog_message, message_id = await self.handle_response_generator(response_generator)
if response_dialog_message.function_call:
await context_manager.add_message(response_dialog_message, -1)
function_name = response_dialog_message.function_call.name
function_args = response_dialog_message.function_call.arguments
function_class = function_storage.get_function_class(function_name)
Expand Down
3 changes: 2 additions & 1 deletion app/bot/settings_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,11 @@ def __init__(self, bot: Bot, dispatcher: Dispatcher, db: DB):
'gpt_mode': ChoiceSetting('GPT mode', 'gpt_mode', list(settings.gpt_mode.keys())),
'use_functions': OnOffSetting('Use functions', 'use_functions'),
'image_generation': OnOffSetting('Image generation', 'image_generation'),
'system_prompt_settings_enabled': OnOffSetting('User info saving', 'system_prompt_settings_enabled'),
'tts-voice': ChoiceSetting('TTS voice', 'tts_voice', TTS_VOICES),
'voice_as_prompt': OnOffSetting('Voice as prompt', 'voice_as_prompt'),
'function_call_verbose': OnOffSetting('Verbose function calls', 'function_call_verbose'),
'streaming_answers': OnOffSetting('Streaming answers', 'streaming_answers'),
# 'streaming_answers': OnOffSetting('Streaming answers', 'streaming_answers'),
# 'auto_summarize': OnOffSetting('Auto summarize', 'auto_summarize'),
# 'forward_as_prompt': OnOffSetting('Forward as prompt', 'forward_as_prompt'),
}
Expand Down
18 changes: 18 additions & 0 deletions app/context/context_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from aiogram import types

import settings
from app.context.dialog_manager import DialogManager
from app.context.function_manager import FunctionManager
from app.openai_helpers.chatgpt import DialogMessage
Expand Down Expand Up @@ -95,6 +96,23 @@ async def add_message(self, dialog_message: DialogMessage, tg_message_id: id) ->
dialog_messages = await self.dialog_manager.add_message_to_dialog(dialog_message, tg_message_id)
return dialog_messages

async def get_system_prompt(self):
gpt_mode = settings.gpt_mode.get(self.user.gpt_mode)
if not gpt_mode:
raise ValueError(f"Unknown GPT mode: {self.user.gpt_mode}")
system_prompt = gpt_mode["system"]

function_storage = await self.get_function_storage()
if function_storage is not None:
system_prompt_addition = function_storage.get_system_prompt_addition()
if system_prompt_addition:
system_prompt += '\n' + system_prompt_addition

if self.user.system_prompt_settings:
system_prompt += f'\n\n<UserSettings>\n{self.user.system_prompt_settings}\n</UserSettings>'

return system_prompt

async def get_context_messages(self) -> List[DialogMessage]:
dialog_messages = self.dialog_manager.get_dialog_messages()
return dialog_messages
Expand Down
4 changes: 4 additions & 0 deletions app/context/function_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import settings
from app.functions.dalle_3 import GenerateImageDalle3
from app.functions.save_user_settings import SaveUserSettings
from app.functions.wolframalpha import QueryWolframAlpha
from app.openai_helpers.function_storage import FunctionStorage
from app.storage.db import DB, User
Expand Down Expand Up @@ -30,6 +31,9 @@ def get_conditional_functions(self):
if self.user.image_generation and check_access_conditions(USER_ROLE_IMAGE_GENERATION, self.user.role):
functions.append(GenerateImageDalle3)

if self.user.system_prompt_settings_enabled:
functions.append(SaveUserSettings)

return functions

async def process_functions(self) -> Optional[FunctionStorage]:
Expand Down
2 changes: 1 addition & 1 deletion app/functions/dalle_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ async def run(self, params: GenerateImageDalle3Params) -> Optional[str]:
tg_caption = caption[:1021] + '...' if len(caption) > 1024 else caption

response = await send_photo(self.message, image_bytes, tg_caption)
text = caption + '\n\nImage:\n<image.png>'
text = 'Generated Image:\n<image.png>'
dialog_message = DialogUtils.prepare_function_response(self.get_name(), text)
await self.context_manager.add_message(dialog_message, response.message_id)
return None
Expand Down
30 changes: 30 additions & 0 deletions app/functions/save_user_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from typing import Optional

from app.bot.utils import send_telegram_message
from app.functions.base import OpenAIFunction, OpenAIFunctionParams
from pydantic import Field


class SaveUserSettingsParams(OpenAIFunctionParams):
settings_text: str = Field(..., description='full list of user info and settings which will apear in <UserSettings> block in system prompt')


class SaveUserSettings(OpenAIFunction):
PARAMS_SCHEMA = SaveUserSettingsParams

async def run(self, params: SaveUserSettingsParams) -> Optional[str]:
self.user.system_prompt_settings = params.settings_text.strip()
await self.db.update_user(self.user)
if self.user.system_prompt_settings:
await send_telegram_message(self.message, f'Saved User Info:\n{params.settings_text}')
else:
await send_telegram_message(self.message, f'Cleared User Info')
return 'success'

@classmethod
def get_name(cls) -> str:
return "save_user_settings"

@classmethod
def get_description(cls) -> str:
return "Save user info or user settings when user asks to do so. Rewrite the text of the UserSettings in system prompt."
24 changes: 8 additions & 16 deletions app/openai_helpers/chatgpt.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ def openai_message(self):
content = self.content
elif isinstance(self.content, list):
content = [part.dict(exclude_none=True) for part in self.content]
elif self.content is None:
content = None
else:
raise ValueError('Unknown type of content')

Expand All @@ -79,20 +81,16 @@ def openai_message(self):


class ChatGPT:
def __init__(self, model="gpt-3.5-turbo", gpt_mode="assistant", function_storage: FunctionStorage = None):
def __init__(self, model, system_prompt: str, function_storage: FunctionStorage = None):
self.function_storage = function_storage
if model not in GPT_MODELS:
raise ValueError(f"Unknown model: {model}")
self.model = model
if gpt_mode not in settings.gpt_mode:
raise ValueError(f"Unknown GPT mode: {gpt_mode}")
self.gpt_mode = gpt_mode
self.system_prompt = system_prompt

async def send_messages(self, messages_to_send: List[DialogMessage]) -> (DialogMessage, CompletionUsage):
additional_fields = {}
system_prompt_addition = None
if self.function_storage is not None:
system_prompt_addition = self.function_storage.get_system_prompt_addition()
additional_fields.update({
'functions': self.function_storage.get_openai_prompt(),
'function_call': 'auto',
Expand All @@ -108,7 +106,7 @@ async def send_messages(self, messages_to_send: List[DialogMessage]) -> (DialogM
if 'functions' in additional_fields:
del additional_fields['functions']

messages = self.create_context(messages_to_send, self.gpt_mode, system_prompt_addition)
messages = self.create_context(messages_to_send, self.system_prompt)
resp = await OpenAIAsync.instance().chat.completions.create(
model=self.model,
messages=messages,
Expand Down Expand Up @@ -144,7 +142,7 @@ async def send_messages_streaming(self, messages_to_send: List[DialogMessage], i
if 'functions' in additional_fields:
del additional_fields['functions']

messages = self.create_context(messages_to_send, self.gpt_mode, system_prompt_addition)
messages = self.create_context(messages_to_send, self.system_prompt)
resp_generator = await OpenAIAsync.instance().chat.completions.create(
model=self.model,
messages=messages,
Expand Down Expand Up @@ -192,15 +190,9 @@ async def send_messages_streaming(self, messages_to_send: List[DialogMessage], i
break

@staticmethod
def create_context(messages: List[DialogMessage], gpt_mode, system_prompt_addition) -> List[Any]:
system_prompt = settings.gpt_mode[gpt_mode]["system"]
if system_prompt_addition:
system_prompt += '\n' + system_prompt_addition

def create_context(messages: List[DialogMessage], system_prompt: str) -> List[Any]:
result = [{"role": "system", "content": system_prompt}]
for dialog_message in messages:
result.append(dialog_message.openai_message())

result += [dialog_message.openai_message() for dialog_message in messages]
return result


Expand Down
2 changes: 1 addition & 1 deletion app/openai_helpers/function_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def get_system_prompt_addition(self) -> str:
addition = function_obj.get_system_prompt_addition()
if addition:
result.append(addition)
return '\n'.join(result)
return '\n' + '\n\n'.join(result)

def get_function_class(self, function_name: str):
function_obj = self.functions.get(function_name)
Expand Down
8 changes: 6 additions & 2 deletions app/storage/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class User(pydantic.BaseModel):
function_call_verbose: bool
image_generation: bool
tts_voice: str
system_prompt_settings: Optional[str]
system_prompt_settings_enabled: Optional[bool]


class MessageType(Enum):
Expand Down Expand Up @@ -76,12 +78,14 @@ async def update_user(self, user: User):
SET current_model = $1, gpt_mode = $2, forward_as_prompt = $3,
voice_as_prompt = $4, use_functions = $5, auto_summarize = $6,
full_name = $7, username = $8, role = $9, streaming_answers = $10,
function_call_verbose = $11, image_generation = $12, tts_voice = $13 WHERE id = $14 RETURNING *'''
function_call_verbose = $11, image_generation = $12, tts_voice = $13,
system_prompt_settings = $14, system_prompt_settings_enabled = $15 WHERE id = $16 RETURNING *'''
return User(**await self.connection_pool.fetchrow(
sql, user.current_model, user.gpt_mode, user.forward_as_prompt,
user.voice_as_prompt, user.use_functions, user.auto_summarize,
user.full_name, user.username, user.role.value, user.streaming_answers,
user.function_call_verbose, user.image_generation, user.tts_voice, user.id,
user.function_call_verbose, user.image_generation, user.tts_voice,
user.system_prompt_settings, user.system_prompt_settings_enabled, user.id,
))

async def create_user(self, telegram_user_id: int, role: UserRole):
Expand Down
4 changes: 4 additions & 0 deletions migrations/sql/0008_add_user_system_prompt_settings.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CREATE SCHEMA IF NOT EXISTS chatgpttg;

ALTER TABLE chatgpttg.user ADD COLUMN IF NOT EXISTS system_prompt_settings text;
ALTER TABLE chatgpttg.user ADD COLUMN IF NOT EXISTS system_prompt_settings_enabled BOOLEAN NOT NULL DEFAULT false;

0 comments on commit ee9f62f

Please sign in to comment.