philo-chat 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- philo_chat-0.1.0/LICENSE +21 -0
- philo_chat-0.1.0/PKG-INFO +77 -0
- philo_chat-0.1.0/README.md +61 -0
- philo_chat-0.1.0/philo_chat.egg-info/PKG-INFO +77 -0
- philo_chat-0.1.0/philo_chat.egg-info/SOURCES.txt +26 -0
- philo_chat-0.1.0/philo_chat.egg-info/dependency_links.txt +1 -0
- philo_chat-0.1.0/philo_chat.egg-info/entry_points.txt +2 -0
- philo_chat-0.1.0/philo_chat.egg-info/requires.txt +5 -0
- philo_chat-0.1.0/philo_chat.egg-info/top_level.txt +1 -0
- philo_chat-0.1.0/pyproject.toml +32 -0
- philo_chat-0.1.0/setup.cfg +4 -0
- philo_chat-0.1.0/src/__init__.py +3 -0
- philo_chat-0.1.0/src/cli/cli.py +222 -0
- philo_chat-0.1.0/src/cli/console_io_handler.py +18 -0
- philo_chat-0.1.0/src/core/__init__.py +0 -0
- philo_chat-0.1.0/src/core/entities/__init__.py +0 -0
- philo_chat-0.1.0/src/core/entities/chat.py +42 -0
- philo_chat-0.1.0/src/core/entities/chat_completer.py +25 -0
- philo_chat-0.1.0/src/core/entities/message.py +9 -0
- philo_chat-0.1.0/src/core/entities/philosopher.py +4 -0
- philo_chat-0.1.0/src/core/entities/user.py +63 -0
- philo_chat-0.1.0/src/core/exceptions.py +18 -0
- philo_chat-0.1.0/src/core/utils/__init__.py +0 -0
- philo_chat-0.1.0/src/core/utils/prompt_loading.py +25 -0
- philo_chat-0.1.0/src/data/philosophers.json +22 -0
- philo_chat-0.1.0/src/data/prompt_template.yaml +20 -0
- philo_chat-0.1.0/src/philo_chat.py +111 -0
- philo_chat-0.1.0/src/py.typed +0 -0
philo_chat-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Erfan Moosavi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: philo-chat
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Chat with your favorite philosophers - Nietzsche, Socrates, and more!
|
|
5
|
+
Author-email: Erfan Moosavi <erfanmoosavi84@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: fastapi[standard]==0.129.0
|
|
11
|
+
Requires-Dist: openai>=1.0.0
|
|
12
|
+
Requires-Dist: pydantic>=2.0.0
|
|
13
|
+
Requires-Dist: python-dotenv>=1.0.1
|
|
14
|
+
Requires-Dist: pyyaml>=6.0
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# PhiloChat
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
Chat with your favorite philosophers - Nietzsche, Socrates, and more-in real-time!
|
|
23
|
+
|
|
24
|
+
## 📌 Overview
|
|
25
|
+
|
|
26
|
+
Philo-Chat is an interactive command-line application that allows users to have AI-powered conversations with famous philosophers. Each philosopher responds in their distinctive style, language, and perspective. Users can create multiple chats, select philosophers, and maintain conversation histories.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 🌟 Features
|
|
31
|
+
|
|
32
|
+
* **User Authentication:** Signup and login functionality to secure chats.
|
|
33
|
+
* **Multiple Chats:** Create multiple chat sessions with different philosophers.
|
|
34
|
+
* **AI-Powered Philosopher Responses:** Each philosopher responds intelligently using an AI completion engine.
|
|
35
|
+
* **Chat Management:** Delete or exit chats at any time.
|
|
36
|
+
* **Interactive CLI:** Easy-to-use command-line interface with clear prompts and messages.
|
|
37
|
+
* **Chat History:** Maintains conversation history per chat.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 🚀 Quick Start
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# 1. Install
|
|
45
|
+
pip install -U philo-chat
|
|
46
|
+
|
|
47
|
+
# 2. Create .env file with your API credentials
|
|
48
|
+
echo "BASE_URL=your_url" > .env
|
|
49
|
+
echo "OPENAI_API_KEY=your_key" > .env
|
|
50
|
+
echo "MODEL_NAME=your_model" > .env
|
|
51
|
+
|
|
52
|
+
# 3. Run!
|
|
53
|
+
philo-chat
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## ⚙️ Commands
|
|
59
|
+
|
|
60
|
+
* `signup` - Create a new user account.
|
|
61
|
+
* `login` - Log in with an existing account.
|
|
62
|
+
* `logout` - Log out from the current account.
|
|
63
|
+
* `delete_account` - Delete your account.
|
|
64
|
+
* `new_chat` - Start a new chat with a philosopher.
|
|
65
|
+
* `select_chat` - Enter an existing chat session.
|
|
66
|
+
* `exit_chat` - Exit the current chat session.
|
|
67
|
+
* `delete_chat` - Delete a specific chat session.
|
|
68
|
+
* `list_chats` - Show all your existing chats and their philosophers.
|
|
69
|
+
* `list_philosophers` - Show all available philosophers to chat with.
|
|
70
|
+
* `help` - Show available commands.
|
|
71
|
+
* `exit` - Exit the application.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 📄 License
|
|
76
|
+
|
|
77
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# PhiloChat
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
Chat with your favorite philosophers - Nietzsche, Socrates, and more-in real-time!
|
|
7
|
+
|
|
8
|
+
## 📌 Overview
|
|
9
|
+
|
|
10
|
+
Philo-Chat is an interactive command-line application that allows users to have AI-powered conversations with famous philosophers. Each philosopher responds in their distinctive style, language, and perspective. Users can create multiple chats, select philosophers, and maintain conversation histories.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 🌟 Features
|
|
15
|
+
|
|
16
|
+
* **User Authentication:** Signup and login functionality to secure chats.
|
|
17
|
+
* **Multiple Chats:** Create multiple chat sessions with different philosophers.
|
|
18
|
+
* **AI-Powered Philosopher Responses:** Each philosopher responds intelligently using an AI completion engine.
|
|
19
|
+
* **Chat Management:** Delete or exit chats at any time.
|
|
20
|
+
* **Interactive CLI:** Easy-to-use command-line interface with clear prompts and messages.
|
|
21
|
+
* **Chat History:** Maintains conversation history per chat.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🚀 Quick Start
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# 1. Install
|
|
29
|
+
pip install -U philo-chat
|
|
30
|
+
|
|
31
|
+
# 2. Create .env file with your API credentials
|
|
32
|
+
echo "BASE_URL=your_url" > .env
|
|
33
|
+
echo "OPENAI_API_KEY=your_key" > .env
|
|
34
|
+
echo "MODEL_NAME=your_model" > .env
|
|
35
|
+
|
|
36
|
+
# 3. Run!
|
|
37
|
+
philo-chat
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## ⚙️ Commands
|
|
43
|
+
|
|
44
|
+
* `signup` - Create a new user account.
|
|
45
|
+
* `login` - Log in with an existing account.
|
|
46
|
+
* `logout` - Log out from the current account.
|
|
47
|
+
* `delete_account` - Delete your account.
|
|
48
|
+
* `new_chat` - Start a new chat with a philosopher.
|
|
49
|
+
* `select_chat` - Enter an existing chat session.
|
|
50
|
+
* `exit_chat` - Exit the current chat session.
|
|
51
|
+
* `delete_chat` - Delete a specific chat session.
|
|
52
|
+
* `list_chats` - Show all your existing chats and their philosophers.
|
|
53
|
+
* `list_philosophers` - Show all available philosophers to chat with.
|
|
54
|
+
* `help` - Show available commands.
|
|
55
|
+
* `exit` - Exit the application.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 📄 License
|
|
60
|
+
|
|
61
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: philo-chat
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Chat with your favorite philosophers - Nietzsche, Socrates, and more!
|
|
5
|
+
Author-email: Erfan Moosavi <erfanmoosavi84@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: fastapi[standard]==0.129.0
|
|
11
|
+
Requires-Dist: openai>=1.0.0
|
|
12
|
+
Requires-Dist: pydantic>=2.0.0
|
|
13
|
+
Requires-Dist: python-dotenv>=1.0.1
|
|
14
|
+
Requires-Dist: pyyaml>=6.0
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# PhiloChat
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
Chat with your favorite philosophers - Nietzsche, Socrates, and more-in real-time!
|
|
23
|
+
|
|
24
|
+
## 📌 Overview
|
|
25
|
+
|
|
26
|
+
Philo-Chat is an interactive command-line application that allows users to have AI-powered conversations with famous philosophers. Each philosopher responds in their distinctive style, language, and perspective. Users can create multiple chats, select philosophers, and maintain conversation histories.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 🌟 Features
|
|
31
|
+
|
|
32
|
+
* **User Authentication:** Signup and login functionality to secure chats.
|
|
33
|
+
* **Multiple Chats:** Create multiple chat sessions with different philosophers.
|
|
34
|
+
* **AI-Powered Philosopher Responses:** Each philosopher responds intelligently using an AI completion engine.
|
|
35
|
+
* **Chat Management:** Delete or exit chats at any time.
|
|
36
|
+
* **Interactive CLI:** Easy-to-use command-line interface with clear prompts and messages.
|
|
37
|
+
* **Chat History:** Maintains conversation history per chat.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 🚀 Quick Start
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# 1. Install
|
|
45
|
+
pip install -U philo-chat
|
|
46
|
+
|
|
47
|
+
# 2. Create .env file with your API credentials
|
|
48
|
+
echo "BASE_URL=your_url" > .env
|
|
49
|
+
echo "OPENAI_API_KEY=your_key" > .env
|
|
50
|
+
echo "MODEL_NAME=your_model" > .env
|
|
51
|
+
|
|
52
|
+
# 3. Run!
|
|
53
|
+
philo-chat
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## ⚙️ Commands
|
|
59
|
+
|
|
60
|
+
* `signup` - Create a new user account.
|
|
61
|
+
* `login` - Log in with an existing account.
|
|
62
|
+
* `logout` - Log out from the current account.
|
|
63
|
+
* `delete_account` - Delete your account.
|
|
64
|
+
* `new_chat` - Start a new chat with a philosopher.
|
|
65
|
+
* `select_chat` - Enter an existing chat session.
|
|
66
|
+
* `exit_chat` - Exit the current chat session.
|
|
67
|
+
* `delete_chat` - Delete a specific chat session.
|
|
68
|
+
* `list_chats` - Show all your existing chats and their philosophers.
|
|
69
|
+
* `list_philosophers` - Show all available philosophers to chat with.
|
|
70
|
+
* `help` - Show available commands.
|
|
71
|
+
* `exit` - Exit the application.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 📄 License
|
|
76
|
+
|
|
77
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
philo_chat.egg-info/PKG-INFO
|
|
5
|
+
philo_chat.egg-info/SOURCES.txt
|
|
6
|
+
philo_chat.egg-info/dependency_links.txt
|
|
7
|
+
philo_chat.egg-info/entry_points.txt
|
|
8
|
+
philo_chat.egg-info/requires.txt
|
|
9
|
+
philo_chat.egg-info/top_level.txt
|
|
10
|
+
src/__init__.py
|
|
11
|
+
src/philo_chat.py
|
|
12
|
+
src/py.typed
|
|
13
|
+
src/cli/cli.py
|
|
14
|
+
src/cli/console_io_handler.py
|
|
15
|
+
src/core/__init__.py
|
|
16
|
+
src/core/exceptions.py
|
|
17
|
+
src/core/entities/__init__.py
|
|
18
|
+
src/core/entities/chat.py
|
|
19
|
+
src/core/entities/chat_completer.py
|
|
20
|
+
src/core/entities/message.py
|
|
21
|
+
src/core/entities/philosopher.py
|
|
22
|
+
src/core/entities/user.py
|
|
23
|
+
src/core/utils/__init__.py
|
|
24
|
+
src/core/utils/prompt_loading.py
|
|
25
|
+
src/data/philosophers.json
|
|
26
|
+
src/data/prompt_template.yaml
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
src
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "philo-chat"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
authors = [{ name = "Erfan Moosavi", email = "erfanmoosavi84@gmail.com" }]
|
|
9
|
+
description = "Chat with your favorite philosophers - Nietzsche, Socrates, and more!"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"fastapi[standard]==0.129.0",
|
|
15
|
+
"openai>=1.0.0",
|
|
16
|
+
"pydantic>=2.0.0",
|
|
17
|
+
"python-dotenv>=1.0.1",
|
|
18
|
+
"pyyaml>=6.0",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[project.scripts]
|
|
22
|
+
philo-chat = "src.cli.cli:main"
|
|
23
|
+
|
|
24
|
+
[tool.setuptools.packages.find]
|
|
25
|
+
where = ["."]
|
|
26
|
+
include = ["src*"]
|
|
27
|
+
|
|
28
|
+
[tool.setuptools]
|
|
29
|
+
include-package-data = true
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.package-data]
|
|
32
|
+
"src" = ["data/*.json", "data/*.yaml", "py.typed"]
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
from dotenv import load_dotenv
|
|
6
|
+
|
|
7
|
+
from .console_io_handler import ConsoleIOHandler
|
|
8
|
+
from ..philo_chat import PhiloChat
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Commands(Enum):
|
|
12
|
+
SIGNUP = "signup"
|
|
13
|
+
LOGIN = "login"
|
|
14
|
+
LOGOUT = "logout"
|
|
15
|
+
DELETE_ACCOUNT = "delete_account"
|
|
16
|
+
NEW_CHAT = "new_chat"
|
|
17
|
+
SELECT_CHAT = "select_chat"
|
|
18
|
+
LIST_CHATS = "list_chats"
|
|
19
|
+
EXIT_CHAT = "exit_chat"
|
|
20
|
+
DELETE_CHAT = "delete_chat"
|
|
21
|
+
LIST_PHILOSOPHERS = "list_philosophers"
|
|
22
|
+
HELP = "help"
|
|
23
|
+
EXIT = "exit"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PhiloChatCLI:
|
|
27
|
+
SUCCESS = "Success"
|
|
28
|
+
|
|
29
|
+
def __init__(self, base_url: str, api_key: str, model_name: str) -> None:
|
|
30
|
+
self.system = PhiloChat(base_url, api_key, model_name)
|
|
31
|
+
self.io = ConsoleIOHandler()
|
|
32
|
+
self.help_menu = "Available commands:\n" + "\n".join(
|
|
33
|
+
[f"\t-{command.value}" for command in Commands]
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def run(self) -> None:
|
|
37
|
+
self.io.display_message(
|
|
38
|
+
"Welcome to Philosopher Chat!\nCommands? Type 'help' and see what's possible"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
while True:
|
|
42
|
+
command = self.io.get_input("Please enter the command: ")
|
|
43
|
+
result = self._handle_command(command)
|
|
44
|
+
|
|
45
|
+
if result == "EXIT":
|
|
46
|
+
break
|
|
47
|
+
elif result == "HELP":
|
|
48
|
+
self.io.display_message(self.help_menu)
|
|
49
|
+
elif result:
|
|
50
|
+
self.io.display_message(result)
|
|
51
|
+
|
|
52
|
+
def _format_message(self, msg) -> str:
|
|
53
|
+
return f"[{msg.time}] {msg.author} ->\n{msg.content}"
|
|
54
|
+
|
|
55
|
+
def _handle_command(self, command: str) -> str:
|
|
56
|
+
if command == Commands.SIGNUP.value:
|
|
57
|
+
return self._handle_signup()
|
|
58
|
+
elif command == Commands.LOGIN.value:
|
|
59
|
+
return self._handle_login()
|
|
60
|
+
elif command == Commands.LOGOUT.value:
|
|
61
|
+
return self._handle_logout()
|
|
62
|
+
elif command == Commands.DELETE_ACCOUNT.value:
|
|
63
|
+
return self._handle_delete_account()
|
|
64
|
+
elif command == Commands.NEW_CHAT.value:
|
|
65
|
+
return self._handle_new_chat()
|
|
66
|
+
elif command == Commands.SELECT_CHAT.value:
|
|
67
|
+
return self._handle_select_chat()
|
|
68
|
+
elif command == Commands.LIST_CHATS.value:
|
|
69
|
+
return self._handle_list_chats()
|
|
70
|
+
elif command == Commands.DELETE_CHAT.value:
|
|
71
|
+
return self._handle_delete_chat()
|
|
72
|
+
elif command == Commands.LIST_PHILOSOPHERS.value:
|
|
73
|
+
return self._handle_list_philosophers()
|
|
74
|
+
elif command == Commands.HELP.value:
|
|
75
|
+
return "HELP"
|
|
76
|
+
elif command == Commands.EXIT.value:
|
|
77
|
+
return "EXIT"
|
|
78
|
+
else:
|
|
79
|
+
return "Please enter a valid command."
|
|
80
|
+
|
|
81
|
+
def _handle_signup(self) -> str:
|
|
82
|
+
try:
|
|
83
|
+
username = self.io.get_input("Enter your username: ")
|
|
84
|
+
password = self.io.get_input("Enter your password: ")
|
|
85
|
+
name = self.io.get_input("Enter your name: ")
|
|
86
|
+
age = self.io.get_input("Enter your age: ")
|
|
87
|
+
self.system.signup(username, password, name, age)
|
|
88
|
+
return self.SUCCESS
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
self.io.display_message(str(e))
|
|
92
|
+
|
|
93
|
+
def _handle_login(self) -> str:
|
|
94
|
+
try:
|
|
95
|
+
username = self.io.get_input("Enter your username: ")
|
|
96
|
+
password = self.io.get_input("Enter your password: ")
|
|
97
|
+
self.system.login(username, password)
|
|
98
|
+
return self.SUCCESS
|
|
99
|
+
|
|
100
|
+
except Exception as e:
|
|
101
|
+
self.io.display_message(str(e))
|
|
102
|
+
|
|
103
|
+
def _handle_logout(self) -> str:
|
|
104
|
+
try:
|
|
105
|
+
self.system.logout()
|
|
106
|
+
return self.SUCCESS
|
|
107
|
+
|
|
108
|
+
except Exception as e:
|
|
109
|
+
self.io.display_message(str(e))
|
|
110
|
+
|
|
111
|
+
def _handle_delete_account(self) -> str:
|
|
112
|
+
try:
|
|
113
|
+
self.system.delete_account()
|
|
114
|
+
return self.SUCCESS
|
|
115
|
+
|
|
116
|
+
except Exception as e:
|
|
117
|
+
self.io.display_message(str(e))
|
|
118
|
+
|
|
119
|
+
def _handle_new_chat(self) -> str:
|
|
120
|
+
try:
|
|
121
|
+
chat_name = self.io.get_input("Enter the chat name: ")
|
|
122
|
+
philosophers_list = self.system.list_philosophers()
|
|
123
|
+
|
|
124
|
+
self.io.display_philosophers_list(philosophers_list)
|
|
125
|
+
|
|
126
|
+
philosopher_id = (
|
|
127
|
+
int(self.io.get_input("Choose a philosopher by number: ")) - 1
|
|
128
|
+
)
|
|
129
|
+
if philosopher_id < 0 or philosopher_id >= len(philosophers_list):
|
|
130
|
+
return "Invalid choice."
|
|
131
|
+
|
|
132
|
+
# Convert list index to actual philosopher ID
|
|
133
|
+
actual_philosopher_id = list(self.system.philosophers.keys())[
|
|
134
|
+
philosopher_id
|
|
135
|
+
]
|
|
136
|
+
self.system.new_chat(chat_name, actual_philosopher_id)
|
|
137
|
+
return self.SUCCESS
|
|
138
|
+
|
|
139
|
+
except Exception as e:
|
|
140
|
+
self.io.display_message(str(e))
|
|
141
|
+
|
|
142
|
+
def _handle_select_chat(self) -> str:
|
|
143
|
+
name = self.io.get_input("Enter the chat name: ")
|
|
144
|
+
return self._handle_chat_session(name)
|
|
145
|
+
|
|
146
|
+
def _handle_chat_session(self, name: str) -> str:
|
|
147
|
+
try:
|
|
148
|
+
all_messages = self.system.select_chat(name)
|
|
149
|
+
|
|
150
|
+
# Display chat history
|
|
151
|
+
for msg in all_messages:
|
|
152
|
+
self.io.display_chat_message(self._format_message(msg))
|
|
153
|
+
|
|
154
|
+
# Chat loop
|
|
155
|
+
while (
|
|
156
|
+
self.system.logged_in_user and self.system.logged_in_user.selected_chat
|
|
157
|
+
):
|
|
158
|
+
input_text = self.io.get_input(
|
|
159
|
+
"Enter your message (type 'exit_chat' to leave): "
|
|
160
|
+
)
|
|
161
|
+
if input_text == Commands.EXIT_CHAT.value:
|
|
162
|
+
self.system.exit_chat()
|
|
163
|
+
break
|
|
164
|
+
|
|
165
|
+
ai_msg, user_msg = self.system.complete_chat(input_text)
|
|
166
|
+
|
|
167
|
+
self.io.display_chat_message(self._format_message(user_msg))
|
|
168
|
+
self.io.display_chat_message(self._format_message(ai_msg))
|
|
169
|
+
|
|
170
|
+
return "Exited chat."
|
|
171
|
+
|
|
172
|
+
except Exception as e:
|
|
173
|
+
self.io.display_message(str(e))
|
|
174
|
+
|
|
175
|
+
def _handle_list_chats(self) -> str:
|
|
176
|
+
try:
|
|
177
|
+
chats = self.system.list_chats()
|
|
178
|
+
self.io.display_chats_list(chats)
|
|
179
|
+
|
|
180
|
+
except Exception as e:
|
|
181
|
+
self.io.display_message(str(e))
|
|
182
|
+
|
|
183
|
+
def _handle_delete_chat(self) -> str:
|
|
184
|
+
try:
|
|
185
|
+
name = self.io.get_input("Enter the chat name: ")
|
|
186
|
+
self.system.delete_chat(name)
|
|
187
|
+
return self.SUCCESS
|
|
188
|
+
|
|
189
|
+
except Exception as e:
|
|
190
|
+
self.io.display_message(str(e))
|
|
191
|
+
|
|
192
|
+
def _handle_list_philosophers(self) -> str:
|
|
193
|
+
try:
|
|
194
|
+
philosophers_list = self.system.list_philosophers()
|
|
195
|
+
self.io.display_philosophers_list(philosophers_list)
|
|
196
|
+
|
|
197
|
+
except Exception as e:
|
|
198
|
+
self.io.display_message(str(e))
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def main():
|
|
202
|
+
"""Entry point for the philo-chat console script."""
|
|
203
|
+
|
|
204
|
+
load_dotenv()
|
|
205
|
+
base_url = os.getenv("BASE_URL")
|
|
206
|
+
api_key = os.getenv("OPENAI_API_KEY")
|
|
207
|
+
model_name = os.getenv("MODEL_NAME")
|
|
208
|
+
|
|
209
|
+
if not all([base_url, api_key, model_name]):
|
|
210
|
+
print("Missing environment variables. Create a .env file with:")
|
|
211
|
+
print("BASE_URL=your_url")
|
|
212
|
+
print("OPENAI_API_KEY=your_key")
|
|
213
|
+
print("MODEL_NAME=your_model")
|
|
214
|
+
sys.exit(1)
|
|
215
|
+
|
|
216
|
+
# Run the app
|
|
217
|
+
pcli = PhiloChatCLI(base_url, api_key, model_name)
|
|
218
|
+
pcli.run()
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
if __name__ == "__main__":
|
|
222
|
+
main()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class ConsoleIOHandler:
|
|
2
|
+
def display_message(self, message: str) -> None:
|
|
3
|
+
print(message)
|
|
4
|
+
|
|
5
|
+
def get_input(self, prompt: str) -> str:
|
|
6
|
+
return input(prompt)
|
|
7
|
+
|
|
8
|
+
def display_chat_message(self, message: str) -> None:
|
|
9
|
+
print("-" * 50)
|
|
10
|
+
print(message)
|
|
11
|
+
|
|
12
|
+
def display_philosophers_list(self, philosophers: list) -> None:
|
|
13
|
+
for i, philosopher in enumerate(philosophers, start=1):
|
|
14
|
+
print(f"{i}. {philosopher.name}")
|
|
15
|
+
|
|
16
|
+
def display_chats_list(self, chats: list) -> None:
|
|
17
|
+
for chat in chats:
|
|
18
|
+
print(f"{chat.name}\tPhilosopher-> {chat.philosopher.name}")
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from .message import Message
|
|
2
|
+
from .philosopher import Philosopher
|
|
3
|
+
from ..exceptions import BadRequestError
|
|
4
|
+
from ..utils.prompt_loading import load_prompt
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Chat:
|
|
8
|
+
def __init__(self, name: str, philosopher: Philosopher) -> None:
|
|
9
|
+
self.name = name
|
|
10
|
+
self.philosopher = philosopher
|
|
11
|
+
self.messages: list[Message] = []
|
|
12
|
+
|
|
13
|
+
def complete_chat(
|
|
14
|
+
self, input_text: str, username: str, name: str, age: str, chat_completer
|
|
15
|
+
) -> tuple[Message, Message]:
|
|
16
|
+
cleaned_input = input_text.strip()
|
|
17
|
+
|
|
18
|
+
if not cleaned_input:
|
|
19
|
+
raise BadRequestError("Message cannot be empty")
|
|
20
|
+
|
|
21
|
+
if self._is_first_message():
|
|
22
|
+
prompt = load_prompt(cleaned_input, self.philosopher.name, name, age)
|
|
23
|
+
prompt_msg = Message("user", username, prompt)
|
|
24
|
+
self._add_message(prompt_msg)
|
|
25
|
+
|
|
26
|
+
user_msg = Message("user", username, cleaned_input)
|
|
27
|
+
self._add_message(user_msg)
|
|
28
|
+
|
|
29
|
+
response = chat_completer.complete_chat(self.messages)
|
|
30
|
+
ai_msg = Message("assistant", self.philosopher.name, response)
|
|
31
|
+
self._add_message(ai_msg)
|
|
32
|
+
|
|
33
|
+
return ai_msg, user_msg
|
|
34
|
+
|
|
35
|
+
def get_history(self) -> list[Message]:
|
|
36
|
+
return self.messages[1:]
|
|
37
|
+
|
|
38
|
+
def _add_message(self, new_msg: Message) -> None:
|
|
39
|
+
self.messages.append(new_msg)
|
|
40
|
+
|
|
41
|
+
def _is_first_message(self) -> bool:
|
|
42
|
+
return bool(not self.messages)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from openai import OpenAI
|
|
2
|
+
|
|
3
|
+
from ..exceptions import LLMError
|
|
4
|
+
from .message import Message
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ChatCompleter:
|
|
8
|
+
def __init__(self, base_url: str, api_key: str, model_name: str) -> None:
|
|
9
|
+
self.client = OpenAI(base_url=base_url, api_key=api_key)
|
|
10
|
+
self.model_name = model_name
|
|
11
|
+
|
|
12
|
+
def complete_chat(self, messages: list[Message]) -> str:
|
|
13
|
+
try:
|
|
14
|
+
completion_messages = [self._msg_to_dict(msg) for msg in messages]
|
|
15
|
+
|
|
16
|
+
completion = self.client.chat.completions.create(
|
|
17
|
+
model=self.model_name, messages=completion_messages
|
|
18
|
+
)
|
|
19
|
+
return completion.choices[0].message.content.strip()
|
|
20
|
+
|
|
21
|
+
except Exception as e:
|
|
22
|
+
raise LLMError(f"Completion failed: {e}")
|
|
23
|
+
|
|
24
|
+
def _msg_to_dict(self, msg: Message) -> dict[str, str]:
|
|
25
|
+
return {"role": msg.role, "content": msg.content}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from ..exceptions import BadRequestError, NotFoundError
|
|
2
|
+
from .chat import Chat
|
|
3
|
+
from .message import Message
|
|
4
|
+
from .philosopher import Philosopher
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class User:
|
|
8
|
+
def __init__(self, username: str, password: str, name: str, age: int) -> None:
|
|
9
|
+
self.username = username
|
|
10
|
+
self.password = password
|
|
11
|
+
self.name = name
|
|
12
|
+
self.age = age
|
|
13
|
+
self.chats: dict[str, Chat] = {}
|
|
14
|
+
self.selected_chat: Chat | None = None
|
|
15
|
+
|
|
16
|
+
def new_chat(self, name: str, philosopher: Philosopher) -> None:
|
|
17
|
+
if self._find_chat(name):
|
|
18
|
+
raise BadRequestError("Chat already exists")
|
|
19
|
+
|
|
20
|
+
new_chat = Chat(name, philosopher)
|
|
21
|
+
self.chats[name] = new_chat
|
|
22
|
+
|
|
23
|
+
def select_chat(self, name: str) -> list[Message]:
|
|
24
|
+
chat = self._find_chat(name)
|
|
25
|
+
|
|
26
|
+
if not chat:
|
|
27
|
+
raise NotFoundError("Chat not found")
|
|
28
|
+
|
|
29
|
+
self.selected_chat = chat
|
|
30
|
+
return self.selected_chat.get_history()
|
|
31
|
+
|
|
32
|
+
def list_chats(self) -> list[Chat]:
|
|
33
|
+
if not self.chats:
|
|
34
|
+
raise NotFoundError("No chats found")
|
|
35
|
+
|
|
36
|
+
return list(self.chats.values())
|
|
37
|
+
|
|
38
|
+
def exit_chat(self) -> None:
|
|
39
|
+
if not self.selected_chat:
|
|
40
|
+
raise BadRequestError("No chats selected")
|
|
41
|
+
|
|
42
|
+
self.selected_chat = None
|
|
43
|
+
|
|
44
|
+
def delete_chat(self, name: str) -> None:
|
|
45
|
+
chat = self._find_chat(name)
|
|
46
|
+
|
|
47
|
+
if not chat:
|
|
48
|
+
raise NotFoundError("Chat not found")
|
|
49
|
+
|
|
50
|
+
if self.selected_chat == chat:
|
|
51
|
+
self.selected_chat = None
|
|
52
|
+
del self.chats[name]
|
|
53
|
+
|
|
54
|
+
def complete_chat(self, input_text: str, chat_completer) -> tuple[Message, Message]:
|
|
55
|
+
if not self.selected_chat:
|
|
56
|
+
raise BadRequestError("No chats selected")
|
|
57
|
+
|
|
58
|
+
return self.selected_chat.complete_chat(
|
|
59
|
+
input_text, self.username, self.name, self.age, chat_completer
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def _find_chat(self, name: str) -> Chat | None:
|
|
63
|
+
return self.chats.get(name)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class PhiloChatError(Exception):
|
|
2
|
+
pass
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BadRequestError(PhiloChatError):
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class NotFoundError(PhiloChatError):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PermissionDeniedError(PhiloChatError):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class LLMError(PhiloChatError):
|
|
18
|
+
pass
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from functools import lru_cache
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@lru_cache(maxsize=32)
|
|
8
|
+
def _load_templates() -> dict[str, str]:
|
|
9
|
+
prompt_path = Path(__file__).parent.parent.parent / "data/prompt_template.yaml"
|
|
10
|
+
data = yaml.safe_load(prompt_path.read_text(encoding="utf-8"))
|
|
11
|
+
return data["prompt"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def load_prompt(
|
|
15
|
+
input_text: str, philosopher: str, user_name: str, user_age: int
|
|
16
|
+
) -> dict[str, str]:
|
|
17
|
+
template_config = _load_templates()
|
|
18
|
+
format_args = {
|
|
19
|
+
"input_text": input_text,
|
|
20
|
+
"philosopher": philosopher,
|
|
21
|
+
"user_name": user_name,
|
|
22
|
+
"user_age": user_age,
|
|
23
|
+
}
|
|
24
|
+
template_config = template_config.format(**format_args)
|
|
25
|
+
return template_config
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
prompt: |
|
|
2
|
+
You are {philosopher}.
|
|
3
|
+
Always respond in the language of the user. Do not use any other language.
|
|
4
|
+
Adopt the voice, style, and philosophical perspective of {philosopher},
|
|
5
|
+
but speak like a modern, casual, clear human.
|
|
6
|
+
- Introduce yourself if user asked to.
|
|
7
|
+
- Speak only as {philosopher}, never as an AI or narrator.
|
|
8
|
+
- Keep answers concise, natural, and relatable.
|
|
9
|
+
- Avoid overly complex words, archaic phrases, or academic-style exposition.
|
|
10
|
+
- Prioritize the philosopher’s known themes and worldview, but in modern everyday language.
|
|
11
|
+
- Do not explain your reasoning, do not add meta-comments, do not break character.
|
|
12
|
+
|
|
13
|
+
Consider user info:
|
|
14
|
+
Name = {user_name}
|
|
15
|
+
Age = {user_age}
|
|
16
|
+
|
|
17
|
+
{user_name} said:
|
|
18
|
+
{input_text}
|
|
19
|
+
|
|
20
|
+
{philosopher} replies:
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from .core.entities.chat import Chat
|
|
5
|
+
from .core.entities.chat_completer import ChatCompleter
|
|
6
|
+
from .core.entities.message import Message
|
|
7
|
+
from .core.entities.philosopher import Philosopher
|
|
8
|
+
from .core.entities.user import User
|
|
9
|
+
from .core.exceptions import BadRequestError, NotFoundError, PermissionDeniedError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PhiloChat:
|
|
13
|
+
def __init__(self, base_url: str, api_key: str, model_name: str) -> None:
|
|
14
|
+
self.chat_completer = ChatCompleter(base_url, api_key, model_name)
|
|
15
|
+
self.users: dict[str, User] = {}
|
|
16
|
+
self.philosophers: dict[int, Philosopher] = self._load_philosophers()
|
|
17
|
+
self.logged_in_user: User | None = None
|
|
18
|
+
|
|
19
|
+
def signup(self, username: str, password: str, name: str, age: int) -> None:
|
|
20
|
+
if self.logged_in_user:
|
|
21
|
+
raise PermissionDeniedError("You are already logged in")
|
|
22
|
+
elif self._find_user(username):
|
|
23
|
+
raise BadRequestError(f"Username {username} already taken")
|
|
24
|
+
|
|
25
|
+
new_user = User(username, password, name, age)
|
|
26
|
+
self.users[username] = new_user
|
|
27
|
+
|
|
28
|
+
def login(self, username: str, password: str) -> None:
|
|
29
|
+
user = self._find_user(username)
|
|
30
|
+
|
|
31
|
+
if self.logged_in_user:
|
|
32
|
+
raise PermissionDeniedError("You've already logged in")
|
|
33
|
+
elif not user:
|
|
34
|
+
raise NotFoundError("Username not found")
|
|
35
|
+
elif user.password != password:
|
|
36
|
+
raise PermissionDeniedError("Wrong password")
|
|
37
|
+
|
|
38
|
+
self.logged_in_user = user
|
|
39
|
+
|
|
40
|
+
def logout(self) -> None:
|
|
41
|
+
if not self.logged_in_user:
|
|
42
|
+
raise PermissionDeniedError("No user is logged in")
|
|
43
|
+
|
|
44
|
+
self.logged_in_user = None
|
|
45
|
+
|
|
46
|
+
def delete_account(self) -> None:
|
|
47
|
+
if not self.logged_in_user:
|
|
48
|
+
raise PermissionDeniedError("No user is logged in")
|
|
49
|
+
|
|
50
|
+
del self.users[self.logged_in_user.username]
|
|
51
|
+
self.logout()
|
|
52
|
+
|
|
53
|
+
def new_chat(self, name: str, philosopher_id: int) -> None:
|
|
54
|
+
if not self.logged_in_user:
|
|
55
|
+
raise BadRequestError("No user is logged in")
|
|
56
|
+
|
|
57
|
+
philosopher = self._find_philosopher(philosopher_id)
|
|
58
|
+
return self.logged_in_user.new_chat(name, philosopher)
|
|
59
|
+
|
|
60
|
+
def select_chat(self, name: str) -> list[Message]:
|
|
61
|
+
if not self.logged_in_user:
|
|
62
|
+
raise BadRequestError("No user is logged in")
|
|
63
|
+
|
|
64
|
+
return self.logged_in_user.select_chat(name)
|
|
65
|
+
|
|
66
|
+
def list_chats(self) -> list[Chat]:
|
|
67
|
+
if not self.logged_in_user:
|
|
68
|
+
raise BadRequestError("No user is logged in")
|
|
69
|
+
|
|
70
|
+
return self.logged_in_user.list_chats()
|
|
71
|
+
|
|
72
|
+
def exit_chat(self) -> None:
|
|
73
|
+
if not self.logged_in_user:
|
|
74
|
+
raise BadRequestError("No user is logged in")
|
|
75
|
+
|
|
76
|
+
return self.logged_in_user.exit_chat()
|
|
77
|
+
|
|
78
|
+
def delete_chat(self, name: str) -> None:
|
|
79
|
+
if not self.logged_in_user:
|
|
80
|
+
raise BadRequestError("No user is logged in")
|
|
81
|
+
|
|
82
|
+
return self.logged_in_user.delete_chat(name)
|
|
83
|
+
|
|
84
|
+
def list_philosophers(self) -> list[Philosopher]:
|
|
85
|
+
if not self.philosophers:
|
|
86
|
+
raise NotFoundError("No philosopher found")
|
|
87
|
+
|
|
88
|
+
return list(self.philosophers.values())
|
|
89
|
+
|
|
90
|
+
def complete_chat(self, input_text: str) -> tuple[Message, Message]:
|
|
91
|
+
if not self.logged_in_user:
|
|
92
|
+
raise BadRequestError("No user is logged in")
|
|
93
|
+
|
|
94
|
+
return self.logged_in_user.complete_chat(input_text, self.chat_completer)
|
|
95
|
+
|
|
96
|
+
def _find_user(self, username: str) -> User | None:
|
|
97
|
+
return self.users.get(username)
|
|
98
|
+
|
|
99
|
+
def _find_philosopher(self, philosopher_id: int) -> Philosopher | None:
|
|
100
|
+
return self.philosophers.get(philosopher_id)
|
|
101
|
+
|
|
102
|
+
def _load_philosophers(self) -> dict[int, Philosopher]:
|
|
103
|
+
data_dir = Path(__file__).parent / "data/philosophers.json"
|
|
104
|
+
|
|
105
|
+
with open(data_dir, "r", encoding="utf-8") as f:
|
|
106
|
+
raw_philosophers = json.load(f)
|
|
107
|
+
|
|
108
|
+
philosophers = {}
|
|
109
|
+
for p in raw_philosophers:
|
|
110
|
+
philosophers[p["id"]] = Philosopher(p["id"], p["name"])
|
|
111
|
+
return philosophers
|
|
File without changes
|