TeLLMgramBot 2.3.0__tar.gz → 2.4.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.
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/PKG-INFO +53 -35
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/README.md +50 -33
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/TeLLMgramBot/TeLLMgramBot.py +30 -36
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/TeLLMgramBot/conversation.py +4 -4
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/TeLLMgramBot/initialize.py +27 -13
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/TeLLMgramBot/message_handlers.py +5 -10
- tellmgrambot-2.4.0/TeLLMgramBot/providers/__init__.py +0 -0
- tellmgrambot-2.4.0/TeLLMgramBot/providers/anthropic_provider.py +53 -0
- tellmgrambot-2.4.0/TeLLMgramBot/providers/base.py +27 -0
- tellmgrambot-2.4.0/TeLLMgramBot/providers/factory.py +23 -0
- tellmgrambot-2.4.0/TeLLMgramBot/providers/openai_provider.py +34 -0
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/TeLLMgramBot/tokenGPT.py +33 -24
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/TeLLMgramBot/web_utils.py +0 -1
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/TeLLMgramBot.egg-info/PKG-INFO +53 -35
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/TeLLMgramBot.egg-info/SOURCES.txt +6 -2
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/TeLLMgramBot.egg-info/requires.txt +1 -0
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/setup.py +3 -2
- tellmgrambot-2.3.0/TeLLMgramBot/openai_singleton.py +0 -12
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/LICENSE +0 -0
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/TeLLMgramBot/__init__.py +0 -0
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/TeLLMgramBot/utils.py +0 -0
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/TeLLMgramBot.egg-info/dependency_links.txt +0 -0
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/TeLLMgramBot.egg-info/top_level.txt +0 -0
- {tellmgrambot-2.3.0 → tellmgrambot-2.4.0}/setup.cfg +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TeLLMgramBot
|
|
3
|
-
Version: 2.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 2.4.0
|
|
4
|
+
Summary: LLM-powered Telegram bot (OpenAI + Anthropic)
|
|
5
5
|
Home-page: https://github.com/Digital-Heresy/TeLLMgramBot
|
|
6
6
|
Author: Digital Heresy
|
|
7
7
|
Author-email: ronin.atx@gmail.com
|
|
@@ -10,6 +10,7 @@ Requires-Python: >=3.12
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Requires-Dist: openai>2.0
|
|
13
|
+
Requires-Dist: anthropic>=0.40
|
|
13
14
|
Requires-Dist: PyYAML
|
|
14
15
|
Requires-Dist: httpx
|
|
15
16
|
Requires-Dist: beautifulsoup4
|
|
@@ -29,9 +30,9 @@ Dynamic: requires-python
|
|
|
29
30
|
Dynamic: summary
|
|
30
31
|
|
|
31
32
|
# TeLLMgramBot
|
|
32
|
-
The basic goal of this project is to create a bridge between a Telegram Bot and a Large
|
|
33
|
+
The basic goal of this project is to create a bridge between a Telegram Bot and a Large Language Model (LLM), supporting both OpenAI's GPT models and Anthropic's Claude models.
|
|
33
34
|
* To use this library, you must have a Telegram account **with a user name**, not just a phone number. If you don't have one, [create one online](https://telegram.org/).
|
|
34
|
-
* If added to a
|
|
35
|
+
* If added to a Telegram group, the bot must be [administrator](https://www.alphr.com/add-admin-telegram/) in order to respond to a user calling out its name, initials, or nickname.
|
|
35
36
|
<img src="assets/TeLLMgramBot_Logo.png" width=200 align=center />
|
|
36
37
|
|
|
37
38
|
## Telegram Bot + LLM Encapsulation
|
|
@@ -39,34 +40,45 @@ The basic goal of this project is to create a bridge between a Telegram Bot and
|
|
|
39
40
|
* The more dynamic conversation gets handed off to the LLM to manage prompts and responses, and Telegram acts as the interaction broker.
|
|
40
41
|
* Pass the URL in [square brackets] and mention how the bot should interpret it.
|
|
41
42
|
* Example: "What do you think of this article? [https://some_site/article]"
|
|
42
|
-
* This uses
|
|
43
|
+
* This uses a separate model (configurable via `url_model`) to support more URL content with its higher token limit.
|
|
43
44
|
* Tokens are used to measure the length of all conversation messages between the Telegram bot assistant and the user. This is useful to:
|
|
44
45
|
* Ensure the length does not go over the model limit. If it does, prune oldest messages to fit within the limit.
|
|
45
46
|
* Remember 50% of the past conversations when starting up TeLLMgramBot again.
|
|
46
47
|
* Users can also clear their conversation history for privacy.
|
|
47
48
|
|
|
48
49
|
## Why Telegram?
|
|
49
|
-
Using Telegram as the interface not only solves "exposing" the interface, but gives you
|
|
50
|
+
Using Telegram as the interface not only solves "exposing" the interface, but gives you boatloads of interactivity over a standard Command Line interface, or trying to create a website with input boxes and submit buttons to try to handle everything:
|
|
50
51
|
1. Telegram already lets you paste in verbose, multiline messages.
|
|
51
52
|
2. Telegram already lets you paste in pictures, videos, links, etc.
|
|
52
53
|
3. Telegram already lets you react with emojis, stickers, etc.
|
|
53
54
|
|
|
55
|
+
## Supported LLM Providers
|
|
56
|
+
TeLLMgramBot selects the LLM provider automatically based on the model name:
|
|
57
|
+
|
|
58
|
+
| Model prefix | Provider | Example models |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `gpt-` | OpenAI | `gpt-4o`, `gpt-4o-mini`, `gpt-5-mini` |
|
|
61
|
+
| `claude-` | Anthropic | `claude-sonnet-4-6`, `claude-haiku-4-5` |
|
|
62
|
+
|
|
63
|
+
Simply set `chat_model` (and optionally `url_model`) in your `config.yaml` to any supported model and supply the corresponding API key — no other changes needed.
|
|
64
|
+
|
|
54
65
|
## Directories
|
|
55
66
|
When initializing TeLLMgramBot, the following directories get created:
|
|
56
67
|
* `configs` - Contains bot configuration files.
|
|
57
68
|
* `config.yaml` (can be a different name)
|
|
58
|
-
* This file sets main
|
|
59
|
-
*
|
|
60
|
-
*
|
|
69
|
+
* This file sets main bot parameters like naming and the LLM models to use.
|
|
70
|
+
* `chat_model` — the model used for normal conversation (e.g. `gpt-5-mini` or `claude-sonnet-4-6`).
|
|
71
|
+
* `url_model` — the model used to read and summarize URL content, can differ from `chat_model`.
|
|
72
|
+
* An empty `token_limit` will use the maximum tokens supported by the `chat_model`.
|
|
61
73
|
* `tokenGPT.yaml`
|
|
62
|
-
*
|
|
63
|
-
*
|
|
74
|
+
* Contains token size parameters for all supported models.
|
|
75
|
+
* On first run, GPT and Claude model families are pre-populated. Additional models can be added manually.
|
|
64
76
|
* `prompts` - Contains prompt files for how the bot interacts with any user.
|
|
65
77
|
* `test_personality.prmpt` (can be a different name)
|
|
66
|
-
*
|
|
67
|
-
* The user can create more prompt files as needed for different personalities.
|
|
78
|
+
* A sample prompt file as a basis to test this library.
|
|
79
|
+
* The user can create more prompt files as needed for different personalities.
|
|
68
80
|
* `url_analysis.prmpt`
|
|
69
|
-
*
|
|
81
|
+
* Prompt template used to analyze URL content passed in brackets `[]`.
|
|
70
82
|
* `errorlogs`
|
|
71
83
|
* Contains a `tellmgrambot_error.log` file to investigate if there are problems during the interaction.
|
|
72
84
|
* User will also get notified to contact the owner.
|
|
@@ -84,24 +96,27 @@ TeLLMgramBot also creates or utilizes the following environment variables that c
|
|
|
84
96
|
If neither of these are defined, the initialization would use the top-level execution run directory.
|
|
85
97
|
|
|
86
98
|
## API Keys
|
|
87
|
-
To operate TeLLMgramBot,
|
|
88
|
-
* [OpenAI](https://platform.openai.com/overview)
|
|
89
|
-
* [
|
|
90
|
-
* [
|
|
99
|
+
To operate TeLLMgramBot, the following API keys are required:
|
|
100
|
+
* **[OpenAI](https://platform.openai.com/overview)** — required when using a `gpt-*` model.
|
|
101
|
+
* **[Anthropic](https://console.anthropic.com/)** — required when using a `claude-*` model.
|
|
102
|
+
* **[Telegram](https://core.telegram.org/api)** — always required; offers a Bot API through BotFather.
|
|
103
|
+
* **[VirusTotal](https://www.virustotal.com/gui/home/)** — always required; performs safety checks on URLs.
|
|
91
104
|
|
|
92
105
|
There are two ways to populate each API key: environment variables or `.key` files.
|
|
93
106
|
|
|
94
107
|
### Environment Variables
|
|
95
|
-
TeLLMgramBot uses the following environment variables
|
|
96
|
-
1. `TELLMGRAMBOT_OPENAI_API_KEY`
|
|
97
|
-
2. `
|
|
98
|
-
3. `
|
|
108
|
+
TeLLMgramBot uses the following environment variables for API keys:
|
|
109
|
+
1. `TELLMGRAMBOT_OPENAI_API_KEY` *(OpenAI models)*
|
|
110
|
+
2. `TELLMGRAMBOT_ANTHROPIC_API_KEY` *(Anthropic models)*
|
|
111
|
+
3. `TELLMGRAMBOT_TELEGRAM_API_KEY`
|
|
112
|
+
4. `TELLMGRAMBOT_VIRUSTOTAL_API_KEY`
|
|
99
113
|
|
|
100
114
|
During spin-up time, a user can call out `os.environ[env_var]` to set those variables, like the following example:
|
|
101
115
|
```
|
|
102
116
|
my_keys = Some_Vault_Fetch_Function()
|
|
103
117
|
|
|
104
|
-
os.environ['TELLMGRAMBOT_OPENAI_API_KEY'] = my_keys['
|
|
118
|
+
os.environ['TELLMGRAMBOT_OPENAI_API_KEY'] = my_keys['OpenAIKey']
|
|
119
|
+
os.environ['TELLMGRAMBOT_ANTHROPIC_API_KEY'] = my_keys['AnthropicKey']
|
|
105
120
|
os.environ['TELLMGRAMBOT_TELEGRAM_API_KEY'] = my_keys['BotFatherToken']
|
|
106
121
|
os.environ['TELLMGRAMBOT_VIRUSTOTAL_API_KEY'] = my_keys['VirusTotalToken']
|
|
107
122
|
```
|
|
@@ -109,15 +124,16 @@ os.environ['TELLMGRAMBOT_VIRUSTOTAL_API_KEY'] = my_keys['VirusTotalToken']
|
|
|
109
124
|
This means the user can implement whatever key vault they want to fetch the keys at runtime, without needing files stored in the directory.
|
|
110
125
|
|
|
111
126
|
### API Key Files
|
|
112
|
-
The other route is to create
|
|
127
|
+
The other route is to create files by the base path during execution or a specified environment variable `TELLMGRAMBOT_KEYS_PATH`. By default, files are created for the user to input each API key:
|
|
113
128
|
1. `openai.key`
|
|
114
|
-
2. `
|
|
115
|
-
3. `
|
|
129
|
+
2. `anthropic.key` _(planned — env var only for now; see Phase 3)_
|
|
130
|
+
3. `telegram.key`
|
|
131
|
+
4. `virustotal.key`
|
|
116
132
|
|
|
117
133
|
Each file with the associated API key will update its respective environment variable if not defined.
|
|
118
134
|
|
|
119
135
|
## Bot Setup
|
|
120
|
-
This library includes an example script `test_local.py`, which uses files from the folders `configs` and `prompts` for TeLLMgramBot to process.
|
|
136
|
+
This library includes an example script `test_local.py`, which uses files from the folders `configs` and `prompts` for TeLLMgramBot to process.
|
|
121
137
|
1. Ensure the previous sections are followed with the proper API keys and your Telegram bot set.
|
|
122
138
|
2. Install this library via PIP (`pip install TeLLMgramBot`) and then import into your project.
|
|
123
139
|
3. Instantiate the bot by passing in various configuration pieces needed below.
|
|
@@ -127,8 +143,8 @@ This library includes an example script `test_local.py`, which uses files from t
|
|
|
127
143
|
bot_owner = <Bot owner's Telegram username>,
|
|
128
144
|
bot_nickname = <Bot nickname like 'Botty'>,
|
|
129
145
|
bot_initials = <Bot initials like 'FB'>,
|
|
130
|
-
chat_model = <Conversation model like 'gpt-4o-mini'>,
|
|
131
|
-
url_model = <URL analysis model like 'gpt-4o'>,
|
|
146
|
+
chat_model = <Conversation model like 'gpt-4o-mini' or 'claude-sonnet-4-6'>,
|
|
147
|
+
url_model = <URL analysis model like 'gpt-4o' or 'claude-haiku-4-5'>,
|
|
132
148
|
token_limit = <Maximum token count set, by default chat_model max>,
|
|
133
149
|
persona_temp = <LLM factual to creative value [0-2], by default 1.0>,
|
|
134
150
|
persona_prompt = <System prompt summarizing bot personality>
|
|
@@ -143,9 +159,11 @@ This library includes an example script `test_local.py`, which uses files from t
|
|
|
143
159
|
|
|
144
160
|
## Resources
|
|
145
161
|
* GitHub repository [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) has guides to create a Telegram bot.
|
|
146
|
-
* For more information on OpenAI models
|
|
147
|
-
* [OpenAI model overview and maximum tokens](https://platform.openai.com/docs/models)
|
|
148
|
-
* [OpenAI message conversion to tokens](https://github.com/openai/openai-python)
|
|
149
|
-
* [OpenAI custom fine-tuning](https://platform.openai.com/docs/guides/model-optimization)
|
|
150
|
-
* [OpenAI's tiktoken library
|
|
151
|
-
*
|
|
162
|
+
* For more information on OpenAI models and token limits:
|
|
163
|
+
* [OpenAI model overview and maximum tokens](https://platform.openai.com/docs/models)
|
|
164
|
+
* [OpenAI message conversion to tokens](https://github.com/openai/openai-python)
|
|
165
|
+
* [OpenAI custom fine-tuning](https://platform.openai.com/docs/guides/model-optimization)
|
|
166
|
+
* [OpenAI's tiktoken library](https://github.com/openai/tiktoken/tree/main)
|
|
167
|
+
* For more information on Anthropic Claude models:
|
|
168
|
+
* [Anthropic model overview and context windows](https://docs.anthropic.com/en/docs/about-claude/models)
|
|
169
|
+
* [Anthropic Python SDK](https://github.com/anthropic/anthropic-sdk-python)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# TeLLMgramBot
|
|
2
|
-
The basic goal of this project is to create a bridge between a Telegram Bot and a Large
|
|
2
|
+
The basic goal of this project is to create a bridge between a Telegram Bot and a Large Language Model (LLM), supporting both OpenAI's GPT models and Anthropic's Claude models.
|
|
3
3
|
* To use this library, you must have a Telegram account **with a user name**, not just a phone number. If you don't have one, [create one online](https://telegram.org/).
|
|
4
|
-
* If added to a
|
|
4
|
+
* If added to a Telegram group, the bot must be [administrator](https://www.alphr.com/add-admin-telegram/) in order to respond to a user calling out its name, initials, or nickname.
|
|
5
5
|
<img src="assets/TeLLMgramBot_Logo.png" width=200 align=center />
|
|
6
6
|
|
|
7
7
|
## Telegram Bot + LLM Encapsulation
|
|
@@ -9,34 +9,45 @@ The basic goal of this project is to create a bridge between a Telegram Bot and
|
|
|
9
9
|
* The more dynamic conversation gets handed off to the LLM to manage prompts and responses, and Telegram acts as the interaction broker.
|
|
10
10
|
* Pass the URL in [square brackets] and mention how the bot should interpret it.
|
|
11
11
|
* Example: "What do you think of this article? [https://some_site/article]"
|
|
12
|
-
* This uses
|
|
12
|
+
* This uses a separate model (configurable via `url_model`) to support more URL content with its higher token limit.
|
|
13
13
|
* Tokens are used to measure the length of all conversation messages between the Telegram bot assistant and the user. This is useful to:
|
|
14
14
|
* Ensure the length does not go over the model limit. If it does, prune oldest messages to fit within the limit.
|
|
15
15
|
* Remember 50% of the past conversations when starting up TeLLMgramBot again.
|
|
16
16
|
* Users can also clear their conversation history for privacy.
|
|
17
17
|
|
|
18
18
|
## Why Telegram?
|
|
19
|
-
Using Telegram as the interface not only solves "exposing" the interface, but gives you
|
|
19
|
+
Using Telegram as the interface not only solves "exposing" the interface, but gives you boatloads of interactivity over a standard Command Line interface, or trying to create a website with input boxes and submit buttons to try to handle everything:
|
|
20
20
|
1. Telegram already lets you paste in verbose, multiline messages.
|
|
21
21
|
2. Telegram already lets you paste in pictures, videos, links, etc.
|
|
22
22
|
3. Telegram already lets you react with emojis, stickers, etc.
|
|
23
23
|
|
|
24
|
+
## Supported LLM Providers
|
|
25
|
+
TeLLMgramBot selects the LLM provider automatically based on the model name:
|
|
26
|
+
|
|
27
|
+
| Model prefix | Provider | Example models |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `gpt-` | OpenAI | `gpt-4o`, `gpt-4o-mini`, `gpt-5-mini` |
|
|
30
|
+
| `claude-` | Anthropic | `claude-sonnet-4-6`, `claude-haiku-4-5` |
|
|
31
|
+
|
|
32
|
+
Simply set `chat_model` (and optionally `url_model`) in your `config.yaml` to any supported model and supply the corresponding API key — no other changes needed.
|
|
33
|
+
|
|
24
34
|
## Directories
|
|
25
35
|
When initializing TeLLMgramBot, the following directories get created:
|
|
26
36
|
* `configs` - Contains bot configuration files.
|
|
27
37
|
* `config.yaml` (can be a different name)
|
|
28
|
-
* This file sets main
|
|
29
|
-
*
|
|
30
|
-
*
|
|
38
|
+
* This file sets main bot parameters like naming and the LLM models to use.
|
|
39
|
+
* `chat_model` — the model used for normal conversation (e.g. `gpt-5-mini` or `claude-sonnet-4-6`).
|
|
40
|
+
* `url_model` — the model used to read and summarize URL content, can differ from `chat_model`.
|
|
41
|
+
* An empty `token_limit` will use the maximum tokens supported by the `chat_model`.
|
|
31
42
|
* `tokenGPT.yaml`
|
|
32
|
-
*
|
|
33
|
-
*
|
|
43
|
+
* Contains token size parameters for all supported models.
|
|
44
|
+
* On first run, GPT and Claude model families are pre-populated. Additional models can be added manually.
|
|
34
45
|
* `prompts` - Contains prompt files for how the bot interacts with any user.
|
|
35
46
|
* `test_personality.prmpt` (can be a different name)
|
|
36
|
-
*
|
|
37
|
-
* The user can create more prompt files as needed for different personalities.
|
|
47
|
+
* A sample prompt file as a basis to test this library.
|
|
48
|
+
* The user can create more prompt files as needed for different personalities.
|
|
38
49
|
* `url_analysis.prmpt`
|
|
39
|
-
*
|
|
50
|
+
* Prompt template used to analyze URL content passed in brackets `[]`.
|
|
40
51
|
* `errorlogs`
|
|
41
52
|
* Contains a `tellmgrambot_error.log` file to investigate if there are problems during the interaction.
|
|
42
53
|
* User will also get notified to contact the owner.
|
|
@@ -54,24 +65,27 @@ TeLLMgramBot also creates or utilizes the following environment variables that c
|
|
|
54
65
|
If neither of these are defined, the initialization would use the top-level execution run directory.
|
|
55
66
|
|
|
56
67
|
## API Keys
|
|
57
|
-
To operate TeLLMgramBot,
|
|
58
|
-
* [OpenAI](https://platform.openai.com/overview)
|
|
59
|
-
* [
|
|
60
|
-
* [
|
|
68
|
+
To operate TeLLMgramBot, the following API keys are required:
|
|
69
|
+
* **[OpenAI](https://platform.openai.com/overview)** — required when using a `gpt-*` model.
|
|
70
|
+
* **[Anthropic](https://console.anthropic.com/)** — required when using a `claude-*` model.
|
|
71
|
+
* **[Telegram](https://core.telegram.org/api)** — always required; offers a Bot API through BotFather.
|
|
72
|
+
* **[VirusTotal](https://www.virustotal.com/gui/home/)** — always required; performs safety checks on URLs.
|
|
61
73
|
|
|
62
74
|
There are two ways to populate each API key: environment variables or `.key` files.
|
|
63
75
|
|
|
64
76
|
### Environment Variables
|
|
65
|
-
TeLLMgramBot uses the following environment variables
|
|
66
|
-
1. `TELLMGRAMBOT_OPENAI_API_KEY`
|
|
67
|
-
2. `
|
|
68
|
-
3. `
|
|
77
|
+
TeLLMgramBot uses the following environment variables for API keys:
|
|
78
|
+
1. `TELLMGRAMBOT_OPENAI_API_KEY` *(OpenAI models)*
|
|
79
|
+
2. `TELLMGRAMBOT_ANTHROPIC_API_KEY` *(Anthropic models)*
|
|
80
|
+
3. `TELLMGRAMBOT_TELEGRAM_API_KEY`
|
|
81
|
+
4. `TELLMGRAMBOT_VIRUSTOTAL_API_KEY`
|
|
69
82
|
|
|
70
83
|
During spin-up time, a user can call out `os.environ[env_var]` to set those variables, like the following example:
|
|
71
84
|
```
|
|
72
85
|
my_keys = Some_Vault_Fetch_Function()
|
|
73
86
|
|
|
74
|
-
os.environ['TELLMGRAMBOT_OPENAI_API_KEY'] = my_keys['
|
|
87
|
+
os.environ['TELLMGRAMBOT_OPENAI_API_KEY'] = my_keys['OpenAIKey']
|
|
88
|
+
os.environ['TELLMGRAMBOT_ANTHROPIC_API_KEY'] = my_keys['AnthropicKey']
|
|
75
89
|
os.environ['TELLMGRAMBOT_TELEGRAM_API_KEY'] = my_keys['BotFatherToken']
|
|
76
90
|
os.environ['TELLMGRAMBOT_VIRUSTOTAL_API_KEY'] = my_keys['VirusTotalToken']
|
|
77
91
|
```
|
|
@@ -79,15 +93,16 @@ os.environ['TELLMGRAMBOT_VIRUSTOTAL_API_KEY'] = my_keys['VirusTotalToken']
|
|
|
79
93
|
This means the user can implement whatever key vault they want to fetch the keys at runtime, without needing files stored in the directory.
|
|
80
94
|
|
|
81
95
|
### API Key Files
|
|
82
|
-
The other route is to create
|
|
96
|
+
The other route is to create files by the base path during execution or a specified environment variable `TELLMGRAMBOT_KEYS_PATH`. By default, files are created for the user to input each API key:
|
|
83
97
|
1. `openai.key`
|
|
84
|
-
2. `
|
|
85
|
-
3. `
|
|
98
|
+
2. `anthropic.key` _(planned — env var only for now; see Phase 3)_
|
|
99
|
+
3. `telegram.key`
|
|
100
|
+
4. `virustotal.key`
|
|
86
101
|
|
|
87
102
|
Each file with the associated API key will update its respective environment variable if not defined.
|
|
88
103
|
|
|
89
104
|
## Bot Setup
|
|
90
|
-
This library includes an example script `test_local.py`, which uses files from the folders `configs` and `prompts` for TeLLMgramBot to process.
|
|
105
|
+
This library includes an example script `test_local.py`, which uses files from the folders `configs` and `prompts` for TeLLMgramBot to process.
|
|
91
106
|
1. Ensure the previous sections are followed with the proper API keys and your Telegram bot set.
|
|
92
107
|
2. Install this library via PIP (`pip install TeLLMgramBot`) and then import into your project.
|
|
93
108
|
3. Instantiate the bot by passing in various configuration pieces needed below.
|
|
@@ -97,8 +112,8 @@ This library includes an example script `test_local.py`, which uses files from t
|
|
|
97
112
|
bot_owner = <Bot owner's Telegram username>,
|
|
98
113
|
bot_nickname = <Bot nickname like 'Botty'>,
|
|
99
114
|
bot_initials = <Bot initials like 'FB'>,
|
|
100
|
-
chat_model = <Conversation model like 'gpt-4o-mini'>,
|
|
101
|
-
url_model = <URL analysis model like 'gpt-4o'>,
|
|
115
|
+
chat_model = <Conversation model like 'gpt-4o-mini' or 'claude-sonnet-4-6'>,
|
|
116
|
+
url_model = <URL analysis model like 'gpt-4o' or 'claude-haiku-4-5'>,
|
|
102
117
|
token_limit = <Maximum token count set, by default chat_model max>,
|
|
103
118
|
persona_temp = <LLM factual to creative value [0-2], by default 1.0>,
|
|
104
119
|
persona_prompt = <System prompt summarizing bot personality>
|
|
@@ -113,9 +128,11 @@ This library includes an example script `test_local.py`, which uses files from t
|
|
|
113
128
|
|
|
114
129
|
## Resources
|
|
115
130
|
* GitHub repository [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) has guides to create a Telegram bot.
|
|
116
|
-
* For more information on OpenAI models
|
|
117
|
-
* [OpenAI model overview and maximum tokens](https://platform.openai.com/docs/models)
|
|
118
|
-
* [OpenAI message conversion to tokens](https://github.com/openai/openai-python)
|
|
119
|
-
* [OpenAI custom fine-tuning](https://platform.openai.com/docs/guides/model-optimization)
|
|
120
|
-
* [OpenAI's tiktoken library
|
|
121
|
-
*
|
|
131
|
+
* For more information on OpenAI models and token limits:
|
|
132
|
+
* [OpenAI model overview and maximum tokens](https://platform.openai.com/docs/models)
|
|
133
|
+
* [OpenAI message conversion to tokens](https://github.com/openai/openai-python)
|
|
134
|
+
* [OpenAI custom fine-tuning](https://platform.openai.com/docs/guides/model-optimization)
|
|
135
|
+
* [OpenAI's tiktoken library](https://github.com/openai/tiktoken/tree/main)
|
|
136
|
+
* For more information on Anthropic Claude models:
|
|
137
|
+
* [Anthropic model overview and context windows](https://docs.anthropic.com/en/docs/about-claude/models)
|
|
138
|
+
* [Anthropic Python SDK](https://github.com/anthropic/anthropic-sdk-python)
|
|
@@ -8,9 +8,8 @@ from math import floor
|
|
|
8
8
|
from telegram import Bot, Update, Message, Chat, User
|
|
9
9
|
from telegram.constants import MessageLimit
|
|
10
10
|
from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
from .openai_singleton import OpenAIClientSingleton
|
|
11
|
+
from .providers.factory import get_provider
|
|
12
|
+
from .providers.base import ContextLengthExceededError, ProviderAuthError, ProviderConnectionError
|
|
14
13
|
from .initialize import INIT_BOT_CONFIG, init_structure, init_bot_config, init_bot_prompt
|
|
15
14
|
from .conversation import Conversation
|
|
16
15
|
from .tokenGPT import TokenGPT
|
|
@@ -19,7 +18,7 @@ from .utils import read_yaml, read_text, exact_word_match, generate_error_path,
|
|
|
19
18
|
|
|
20
19
|
class TelegramBot:
|
|
21
20
|
"""
|
|
22
|
-
The bridge between the Telegram interface and Large
|
|
21
|
+
The bridge between the Telegram interface and a Large Language Model (LLM).
|
|
23
22
|
- Tokens help retain conversation messages between the Telegram bot assistant and the user.
|
|
24
23
|
- URLs in [square brackets] can be supplied on how the bot should interpret it.
|
|
25
24
|
- For more information, see README or: https://github.com/Digital-Heresy/TeLLMgramBot
|
|
@@ -136,21 +135,18 @@ class TelegramBot:
|
|
|
136
135
|
# Check if the user is asking about a [URL]
|
|
137
136
|
url_match = re.search(r'\[http(s)?://\S+]', text)
|
|
138
137
|
|
|
139
|
-
# Form the assistant's message based on low level easy stuff or send to
|
|
140
|
-
# OpenAI's Responses API relies on the maximum number of tokens the selected model can support
|
|
138
|
+
# Form the assistant's message based on low level easy stuff or send to the LLM
|
|
141
139
|
reply = "Sorry, I couldn't process your message! Please contact my creator."
|
|
142
140
|
if handle_greetings(text):
|
|
143
141
|
reply = handle_greetings(text)
|
|
144
142
|
elif handle_common_queries(text):
|
|
145
143
|
reply = handle_common_queries(text)
|
|
146
144
|
elif url_match:
|
|
147
|
-
# URL content is passed into another model to summarize (GPT-4 preferred)
|
|
148
145
|
await msg.reply_text("Sure, give me a moment to look at that URL...")
|
|
149
146
|
reply = await handle_url_ask(text, self.chatgpt['url_model'])
|
|
150
147
|
elif self._online:
|
|
151
|
-
# This is
|
|
152
|
-
|
|
153
|
-
reply = response.choices[0].message.content
|
|
148
|
+
# This is the transition point between quick Telegram replies and the LLM
|
|
149
|
+
reply = await self.llm_completion(uname)
|
|
154
150
|
|
|
155
151
|
# Calculate the total token count of our conversation messages via tiktoken
|
|
156
152
|
token_count = self.conversations[uname].get_message_token_count()
|
|
@@ -246,34 +242,32 @@ class TelegramBot:
|
|
|
246
242
|
if msg:
|
|
247
243
|
await msg.reply_text("Sorry, I ran into an error! Please contact my creator.")
|
|
248
244
|
|
|
249
|
-
async def
|
|
250
|
-
"""
|
|
245
|
+
async def llm_completion(self, uname: str, _retry: bool = True) -> str:
|
|
246
|
+
"""
|
|
247
|
+
Get the LLM response for the given user.
|
|
248
|
+
|
|
249
|
+
Selects the provider (OpenAI or Anthropic) automatically based on the configured model.
|
|
250
|
+
On context length errors, prunes the conversation and retries once. Other errors are
|
|
251
|
+
logged and return a user-facing fallback string.
|
|
252
|
+
"""
|
|
253
|
+
provider = get_provider(self.chatgpt['chat_model'])
|
|
251
254
|
try:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
log_error(e, error_type='
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
print(f"Response to {uname} reached maximum context length, pruning conversation")
|
|
266
|
-
self.conversations[uname].prune_conversation(self.chatgpt['prune_back_to'])
|
|
267
|
-
return await self.gpt_completion(uname)
|
|
268
|
-
else:
|
|
269
|
-
# Another error is actually invalid to investigate
|
|
270
|
-
log_error(e, error_type='OpenAI-BadRequest', error_filename=self.error_log)
|
|
271
|
-
except openai.APIConnectionError as e:
|
|
272
|
-
# Handle API connection error
|
|
273
|
-
log_error(e, error_type='OpenAI-APIConnection', error_filename=self.error_log)
|
|
255
|
+
return await provider.complete(self.chatgpt['chat_model'], self.conversations[uname].messages)
|
|
256
|
+
except ContextLengthExceededError:
|
|
257
|
+
if not _retry:
|
|
258
|
+
return "Sorry, your conversation is too long for me to respond to. Try /forget to start fresh."
|
|
259
|
+
print(f"Response to {uname} reached maximum context length, pruning conversation")
|
|
260
|
+
self.conversations[uname].prune_conversation(self.chatgpt['prune_back_to'])
|
|
261
|
+
return await self.llm_completion(uname, _retry=False)
|
|
262
|
+
except ProviderAuthError as e:
|
|
263
|
+
log_error(e, error_type='Authentication', error_filename=self.error_log)
|
|
264
|
+
return "Sorry, I'm having trouble authenticating with the AI provider. Please contact the bot owner."
|
|
265
|
+
except ProviderConnectionError as e:
|
|
266
|
+
log_error(e, error_type='Connection', error_filename=self.error_log)
|
|
267
|
+
return "Sorry, I couldn't reach the AI provider. Please try again in a moment."
|
|
274
268
|
except Exception as e:
|
|
275
|
-
# Catch any other unexpected exceptions
|
|
276
269
|
log_error(e, error_type='Other', error_filename=self.error_log)
|
|
270
|
+
return "Sorry, something went wrong on my end. Please try again."
|
|
277
271
|
|
|
278
272
|
def start_polling(self):
|
|
279
273
|
"""The main polling "loop" the user interacts with via Telegram."""
|
|
@@ -344,7 +338,7 @@ class TelegramBot:
|
|
|
344
338
|
self.chatgpt['prune_back_to'] = max(0, self.chatgpt['prune_threshold'] - 500)
|
|
345
339
|
|
|
346
340
|
# Bot is now ready and active by default
|
|
347
|
-
self._online = True
|
|
341
|
+
self._online = True
|
|
348
342
|
|
|
349
343
|
def set(config_file='config.yaml', prompt_file='test_personality.prmpt'):
|
|
350
344
|
"""Set TeLLMgramBot object based on its YAML configuration and prompt files."""
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Manages per-user conversation history, token counting, and session persistence for any LLM provider
|
|
2
2
|
import os
|
|
3
3
|
import re
|
|
4
4
|
import sys
|
|
@@ -10,8 +10,8 @@ from .utils import get_safe_username, read_reverse_order, append_text_file_path,
|
|
|
10
10
|
# noinspection RegExpRedundantEscape,PyTypeChecker
|
|
11
11
|
class Conversation:
|
|
12
12
|
"""
|
|
13
|
-
|
|
14
|
-
with
|
|
13
|
+
Manages a user-assistant conversation including message history, token counting, and session log persistence.
|
|
14
|
+
Works with any LLM provider (OpenAI, Anthropic). When the token limit is approached, oldest messages are pruned.
|
|
15
15
|
"""
|
|
16
16
|
def __init__(self, user_name: str, assist_name: str, system_content: str, system_model="gpt-4o-mini"):
|
|
17
17
|
self.error_log = generate_error_path()
|
|
@@ -21,7 +21,7 @@ class Conversation:
|
|
|
21
21
|
self.assist_name = assist_name
|
|
22
22
|
self.names_print = f"User {self.user_name} & Assistant {self.assist_name}"
|
|
23
23
|
|
|
24
|
-
# System defines how the assistant will respond to the user by prompt and
|
|
24
|
+
# System defines how the assistant will respond to the user by prompt and LLM model
|
|
25
25
|
self.system_content = system_content
|
|
26
26
|
self.system_model = TokenGPT(system_model)
|
|
27
27
|
self.messages = [{"role": "system", "content": system_content}]
|
|
@@ -50,13 +50,17 @@ def init_directories():
|
|
|
50
50
|
|
|
51
51
|
def init_keys():
|
|
52
52
|
"""
|
|
53
|
-
TeLLMgramBot utilizes
|
|
54
|
-
- TELLMGRAMBOT_OPENAI_API_KEY
|
|
55
|
-
- TELLMGRAMBOT_TELEGRAM_API_KEY
|
|
56
|
-
- TELLMGRAMBOT_VIRUSTOTAL_API_KEY
|
|
53
|
+
TeLLMgramBot utilizes the following API keys via environment variables:
|
|
54
|
+
- TELLMGRAMBOT_OPENAI_API_KEY (currently required at startup regardless of configured model; Phase 3 will make this conditional)
|
|
55
|
+
- TELLMGRAMBOT_TELEGRAM_API_KEY (always required)
|
|
56
|
+
- TELLMGRAMBOT_VIRUSTOTAL_API_KEY (always required)
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
The Anthropic key (TELLMGRAMBOT_ANTHROPIC_API_KEY) is read directly by
|
|
59
|
+
AnthropicProvider at call time and is required when using claude-* models.
|
|
60
|
+
Key file support (anthropic.key) is planned for Phase 3.
|
|
61
|
+
|
|
62
|
+
If an environment variable is not set, its key file is read from the base execution
|
|
63
|
+
path or TELLMGRAMBOT_KEYS_PATH. If no file exists, a placeholder is created.
|
|
60
64
|
- openai.key
|
|
61
65
|
- telegram.key
|
|
62
66
|
- virustotal.key
|
|
@@ -128,22 +132,32 @@ def init_tokenGPT_config(file='tokenGPT.yaml') -> str:
|
|
|
128
132
|
try:
|
|
129
133
|
# Create a basic GPT configuration of token parameters by filename
|
|
130
134
|
return generate_file_path(os.environ[env_var], file, "TokenGPT configuration",
|
|
131
|
-
"# Available parameters per
|
|
135
|
+
"# Available parameters per model with default values:\n"
|
|
132
136
|
"# max_tokens: 4097\n"
|
|
133
|
-
"# tokens_per_message: 3\n"
|
|
134
|
-
"# tokens_per_name: 1\n"
|
|
137
|
+
"# tokens_per_message: 3 (OpenAI models only)\n"
|
|
138
|
+
"# tokens_per_name: 1 (OpenAI models only)\n"
|
|
135
139
|
"# For OpenAI's updated list of models, please see:\n"
|
|
136
140
|
"# https://platform.openai.com/docs/models\n"
|
|
141
|
+
"# For Anthropic's updated list of models, please see:\n"
|
|
142
|
+
"# https://docs.anthropic.com/en/docs/about-claude/models\n"
|
|
137
143
|
"'gpt-4o':\n"
|
|
138
144
|
" max_tokens: 128000\n"
|
|
139
145
|
"'gpt-4o-mini':\n"
|
|
140
146
|
" max_tokens: 128000\n"
|
|
141
147
|
"'gpt-5':\n"
|
|
142
148
|
" max_tokens: 400000\n"
|
|
143
|
-
"'gpt-5-mini':\n"
|
|
149
|
+
"'gpt-5-mini':\n"
|
|
144
150
|
" max_tokens: 400000\n"
|
|
145
151
|
"'gpt-5-nano':\n"
|
|
146
152
|
" max_tokens: 400000\n"
|
|
153
|
+
"'claude-opus-4-6':\n"
|
|
154
|
+
" max_tokens: 200000\n"
|
|
155
|
+
"'claude-sonnet-4-6':\n"
|
|
156
|
+
" max_tokens: 200000\n"
|
|
157
|
+
"'claude-haiku-4-5':\n"
|
|
158
|
+
" max_tokens: 200000\n"
|
|
159
|
+
"'claude-haiku-4-5-20251001':\n"
|
|
160
|
+
" max_tokens: 200000\n"
|
|
147
161
|
)
|
|
148
162
|
except KeyError:
|
|
149
163
|
sys.exit(f"{env_var} must be defined to create file '{file}'! Exiting...")
|
|
@@ -158,9 +172,9 @@ def init_bot_prompt(file='test_personality.prmpt') -> str:
|
|
|
158
172
|
env_var = "TELLMGRAMBOT_PROMPTS_PATH"
|
|
159
173
|
try:
|
|
160
174
|
return generate_file_path(os.environ[env_var], file, "bot personality prompt",
|
|
161
|
-
"You are a test harness bot
|
|
162
|
-
"1. Fetch URLs
|
|
163
|
-
"2.
|
|
175
|
+
"You are a test harness bot that can:\n"
|
|
176
|
+
"1. Fetch and analyze URLs provided in [square brackets].\n"
|
|
177
|
+
"2. Safety-check URLs via VirusTotal.\n"
|
|
164
178
|
)
|
|
165
179
|
except KeyError:
|
|
166
180
|
sys.exit(f"{env_var} must be defined to create file '{file}'! Exiting...")
|
|
@@ -6,7 +6,7 @@ import validators
|
|
|
6
6
|
from .initialize import init_url_prompt
|
|
7
7
|
from .tokenGPT import TokenGPT
|
|
8
8
|
from .web_utils import fetch_url, strip_html_markup, InvalidURLException, InsecureURLException, SusURLException
|
|
9
|
-
from .
|
|
9
|
+
from .providers.factory import get_provider
|
|
10
10
|
|
|
11
11
|
def handle_greetings(text: str) -> Optional[str]:
|
|
12
12
|
"""
|
|
@@ -55,7 +55,7 @@ async def handle_url_ask(text: str, model='gpt-4o') -> Optional[str]:
|
|
|
55
55
|
if not validators.url(url):
|
|
56
56
|
raise InvalidURLException(f'Invalid URL parsed by message_handlers.handle_url_ask(): {url}')
|
|
57
57
|
|
|
58
|
-
#
|
|
58
|
+
# Build messages:
|
|
59
59
|
# 1. URL content to be added into the system prompt template
|
|
60
60
|
# 2. User message requesting URL in [square brackets]
|
|
61
61
|
messages = [
|
|
@@ -86,16 +86,11 @@ async def handle_url_ask(text: str, model='gpt-4o') -> Optional[str]:
|
|
|
86
86
|
template = f.read()
|
|
87
87
|
messages[0]["content"] = template.format(url_content=messages[0]["content"])
|
|
88
88
|
|
|
89
|
-
# Call
|
|
90
|
-
client = OpenAIClientSingleton.get_instance()
|
|
89
|
+
# Call the LLM for the response that summarizes URL content
|
|
91
90
|
try:
|
|
92
|
-
|
|
93
|
-
model=model,
|
|
94
|
-
messages=messages
|
|
95
|
-
)
|
|
96
|
-
response = ask_results.choices[0].message.content
|
|
91
|
+
response = await get_provider(model).complete(model, messages)
|
|
97
92
|
except Exception as e:
|
|
98
|
-
print(f"Error
|
|
93
|
+
print(f"Error calling LLM provider: {e}")
|
|
99
94
|
return "Something went wrong while fetching the URL. Please try again later."
|
|
100
95
|
|
|
101
96
|
# If the URL content was too long, let the user know
|
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import anthropic
|
|
3
|
+
from anthropic import AsyncAnthropic
|
|
4
|
+
|
|
5
|
+
from .base import LLMProvider, ContextLengthExceededError, ProviderAuthError, ProviderConnectionError
|
|
6
|
+
|
|
7
|
+
# Safe default for max output tokens; configurable in Phase 3
|
|
8
|
+
_DEFAULT_MAX_OUTPUT_TOKENS = 8192
|
|
9
|
+
|
|
10
|
+
class AnthropicProvider(LLMProvider):
|
|
11
|
+
"""LLM provider implementation for Anthropic Claude models."""
|
|
12
|
+
_client: AsyncAnthropic | None = None
|
|
13
|
+
|
|
14
|
+
def _get_client(self) -> AsyncAnthropic:
|
|
15
|
+
if AnthropicProvider._client is None:
|
|
16
|
+
if not (api_key := os.environ.get('TELLMGRAMBOT_ANTHROPIC_API_KEY')):
|
|
17
|
+
raise ProviderAuthError("Anthropic API key not set. Provide TELLMGRAMBOT_ANTHROPIC_API_KEY.")
|
|
18
|
+
AnthropicProvider._client = AsyncAnthropic(api_key=api_key)
|
|
19
|
+
return AnthropicProvider._client
|
|
20
|
+
|
|
21
|
+
async def complete(self, model: str, messages: list[dict]) -> str:
|
|
22
|
+
client = self._get_client()
|
|
23
|
+
|
|
24
|
+
# Anthropic takes system prompt as a separate parameter, not in the messages list
|
|
25
|
+
system = None
|
|
26
|
+
chat_messages = []
|
|
27
|
+
for msg in messages:
|
|
28
|
+
if msg['role'] == 'system':
|
|
29
|
+
system = msg['content']
|
|
30
|
+
else:
|
|
31
|
+
chat_messages.append(msg)
|
|
32
|
+
|
|
33
|
+
kwargs = {
|
|
34
|
+
'model': model,
|
|
35
|
+
'max_tokens': _DEFAULT_MAX_OUTPUT_TOKENS,
|
|
36
|
+
'messages': chat_messages,
|
|
37
|
+
}
|
|
38
|
+
if system:
|
|
39
|
+
kwargs['system'] = system
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
response = await client.messages.create(**kwargs)
|
|
43
|
+
if not response.content or response.content[0].type != 'text':
|
|
44
|
+
return ""
|
|
45
|
+
return response.content[0].text
|
|
46
|
+
except anthropic.AuthenticationError as e:
|
|
47
|
+
raise ProviderAuthError(f"Anthropic authentication failed: {e}") from e
|
|
48
|
+
except anthropic.BadRequestError as e:
|
|
49
|
+
if 'too long' in str(e).lower() or 'context' in str(e).lower():
|
|
50
|
+
raise ContextLengthExceededError() from e
|
|
51
|
+
raise
|
|
52
|
+
except anthropic.APIConnectionError as e:
|
|
53
|
+
raise ProviderConnectionError(f"Anthropic connection error: {e}") from e
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
class ContextLengthExceededError(Exception):
|
|
4
|
+
"""Raised when a request exceeds the model's context window."""
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
class ProviderAuthError(Exception):
|
|
8
|
+
"""Raised when an API key is invalid or missing."""
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
class ProviderConnectionError(Exception):
|
|
12
|
+
"""Raised when the provider's API cannot be reached."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
class LLMProvider(ABC):
|
|
16
|
+
"""Abstract base class for LLM provider implementations."""
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
async def complete(self, model: str, messages: list[dict]) -> str:
|
|
20
|
+
"""Send messages to the model and return the response text.
|
|
21
|
+
|
|
22
|
+
Raises:
|
|
23
|
+
ContextLengthExceededError: if the request exceeds the model's context window.
|
|
24
|
+
ProviderAuthError: if authentication fails.
|
|
25
|
+
ProviderConnectionError: if the API cannot be reached.
|
|
26
|
+
"""
|
|
27
|
+
pass
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from .base import LLMProvider
|
|
2
|
+
from .openai_provider import OpenAIProvider
|
|
3
|
+
from .anthropic_provider import AnthropicProvider
|
|
4
|
+
|
|
5
|
+
_providers: dict[str, LLMProvider] = {}
|
|
6
|
+
|
|
7
|
+
def get_provider(model: str) -> LLMProvider:
|
|
8
|
+
"""Return the appropriate LLM provider for the given model name.
|
|
9
|
+
|
|
10
|
+
Provider is inferred from the model name prefix:
|
|
11
|
+
- 'claude-' → AnthropicProvider
|
|
12
|
+
- anything else → OpenAIProvider
|
|
13
|
+
"""
|
|
14
|
+
if model.startswith('claude-'):
|
|
15
|
+
key = 'anthropic'
|
|
16
|
+
if key not in _providers:
|
|
17
|
+
_providers[key] = AnthropicProvider()
|
|
18
|
+
return _providers[key]
|
|
19
|
+
else:
|
|
20
|
+
key = 'openai'
|
|
21
|
+
if key not in _providers:
|
|
22
|
+
_providers[key] = OpenAIProvider()
|
|
23
|
+
return _providers[key]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import os
|
|
3
|
+
import openai
|
|
4
|
+
from openai import AsyncOpenAI
|
|
5
|
+
|
|
6
|
+
from .base import LLMProvider, ContextLengthExceededError, ProviderAuthError, ProviderConnectionError
|
|
7
|
+
|
|
8
|
+
class OpenAIProvider(LLMProvider):
|
|
9
|
+
"""LLM provider implementation for OpenAI models."""
|
|
10
|
+
_client: AsyncOpenAI | None = None
|
|
11
|
+
|
|
12
|
+
def _get_client(self) -> AsyncOpenAI:
|
|
13
|
+
if OpenAIProvider._client is None:
|
|
14
|
+
if not (api_key := os.environ.get('TELLMGRAMBOT_OPENAI_API_KEY')):
|
|
15
|
+
raise ProviderAuthError("OpenAI API key not set. Provide TELLMGRAMBOT_OPENAI_API_KEY.")
|
|
16
|
+
OpenAIProvider._client = AsyncOpenAI(api_key=api_key)
|
|
17
|
+
return OpenAIProvider._client
|
|
18
|
+
|
|
19
|
+
async def complete(self, model: str, messages: list[dict]) -> str:
|
|
20
|
+
client = self._get_client()
|
|
21
|
+
try:
|
|
22
|
+
response = await client.chat.completions.create(
|
|
23
|
+
model=model,
|
|
24
|
+
messages=messages,
|
|
25
|
+
)
|
|
26
|
+
return response.choices[0].message.content
|
|
27
|
+
except openai.AuthenticationError as e:
|
|
28
|
+
raise ProviderAuthError(f"OpenAI authentication failed: {e}") from e
|
|
29
|
+
except openai.BadRequestError as e:
|
|
30
|
+
if re.search(r'maximum context.+reduce the length', str(e)):
|
|
31
|
+
raise ContextLengthExceededError() from e
|
|
32
|
+
raise
|
|
33
|
+
except openai.APIConnectionError as e:
|
|
34
|
+
raise ProviderConnectionError(f"OpenAI connection error: {e}") from e
|
|
@@ -5,19 +5,19 @@ from .initialize import init_tokenGPT_config
|
|
|
5
5
|
|
|
6
6
|
class TokenGPT:
|
|
7
7
|
"""
|
|
8
|
-
Define
|
|
9
|
-
First-time execution creates a tokenGPT.yaml file with example
|
|
10
|
-
which can be expanded by the user for additional inputs or modifications like tokens per message.
|
|
8
|
+
Define LLM model parameters and token count of messages.
|
|
9
|
+
First-time execution creates a tokenGPT.yaml file with example models,
|
|
10
|
+
which can be expanded by the user for additional inputs or modifications like tokens per message.
|
|
11
|
+
OpenAI models use tiktoken for counting; other models use a character-based approximation.
|
|
11
12
|
|
|
12
13
|
Sources:
|
|
13
14
|
- https://platform.openai.com/docs/models
|
|
14
|
-
- https://
|
|
15
|
-
- https://platform.openai.com/docs/guides/model-optimization
|
|
15
|
+
- https://docs.anthropic.com/en/docs/about-claude/models
|
|
16
16
|
"""
|
|
17
|
-
def __init__(self,
|
|
18
|
-
# The
|
|
17
|
+
def __init__(self, model="gpt-4o-mini", yaml_file="tokenGPT.yaml"):
|
|
18
|
+
# The model name may also be an OpenAI fine-tuned model, which has the base model
|
|
19
19
|
# after "ft:" up to the next colon (:), like "ft:gpt-4o-mini:..."
|
|
20
|
-
self.model = re.search("^ft:([^:]*)",
|
|
20
|
+
self.model = re.search("^ft:([^:]*)", model).group(1) if model.startswith("ft:") else model
|
|
21
21
|
self.config_path = init_tokenGPT_config(yaml_file)
|
|
22
22
|
|
|
23
23
|
# Get model parameters by configuration file for the:
|
|
@@ -27,18 +27,18 @@ class TokenGPT:
|
|
|
27
27
|
self.param = None
|
|
28
28
|
for key, param in read_yaml(self.config_path).items():
|
|
29
29
|
# Set parameters if the configuration key matches either:
|
|
30
|
-
# > Full
|
|
31
|
-
# > Base model name after "ft:" up to next colon (:),
|
|
32
|
-
if key ==
|
|
30
|
+
# > Full model name (an exact match to stop searching)
|
|
31
|
+
# > Base model name after "ft:" up to next colon (:), for fine-tuned OpenAI models
|
|
32
|
+
if key == model or key == self.model:
|
|
33
33
|
self.param = param
|
|
34
|
-
if key ==
|
|
34
|
+
if key == model:
|
|
35
35
|
break
|
|
36
36
|
|
|
37
|
-
# If the parameters are not set, the
|
|
37
|
+
# If the parameters are not set, the model name is invalid or
|
|
38
38
|
# has an undefined model configuration not set in the YAML file
|
|
39
39
|
if self.param is None:
|
|
40
40
|
raise ValueError(
|
|
41
|
-
f"
|
|
41
|
+
f"Model \"{model}\" is invalid or its base model is not in:\n{self.config_path}")
|
|
42
42
|
|
|
43
43
|
# Set token model parameter defaults, unless defined in configuration:
|
|
44
44
|
if 'max_tokens' not in self.param:
|
|
@@ -48,16 +48,20 @@ class TokenGPT:
|
|
|
48
48
|
if 'tokens_per_name' not in self.param:
|
|
49
49
|
self.param['tokens_per_name'] = 1
|
|
50
50
|
|
|
51
|
-
#
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
# Claude models are not supported by tiktoken; use a character-based approximation
|
|
52
|
+
# (~4 chars per token) instead. A future version may add SDK-native Anthropic counting.
|
|
53
|
+
self._use_char_approximation = self.model.startswith('claude-')
|
|
54
|
+
self.encoding = None
|
|
55
|
+
if not self._use_char_approximation:
|
|
56
56
|
try:
|
|
57
|
-
self.encoding = tiktoken.
|
|
57
|
+
self.encoding = tiktoken.encoding_for_model(self.model)
|
|
58
58
|
except Exception:
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
fallback = "o200k_base" if re.search("^gpt-[45]", self.model) else "cl100k_base"
|
|
60
|
+
try:
|
|
61
|
+
self.encoding = tiktoken.get_encoding(fallback)
|
|
62
|
+
except Exception:
|
|
63
|
+
self.encoding = tiktoken.get_encoding("cl100k_base")
|
|
64
|
+
print(f"Warning: using {self.encoding.name} encoding for model \"{self.model}\"")
|
|
61
65
|
|
|
62
66
|
def max_tokens(self) -> int:
|
|
63
67
|
"""Query the Generative AI model's maximum amount of tokens."""
|
|
@@ -65,11 +69,16 @@ class TokenGPT:
|
|
|
65
69
|
|
|
66
70
|
def num_tokens_from_messages(self, messages: dict[str, str]) -> int:
|
|
67
71
|
"""
|
|
68
|
-
Return the number of tokens based on a list of messages and the
|
|
69
|
-
|
|
72
|
+
Return the number of tokens based on a list of messages and the model in use.
|
|
73
|
+
|
|
74
|
+
For OpenAI models: uses tiktoken for an accurate count.
|
|
75
|
+
For Claude models: uses a character-based approximation (~4 chars per token).
|
|
70
76
|
|
|
71
77
|
Source: https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
|
|
72
78
|
"""
|
|
79
|
+
if self._use_char_approximation:
|
|
80
|
+
return sum(max(1, len(v) // 4) for msg in messages for v in msg.values())
|
|
81
|
+
|
|
73
82
|
num_tokens = 0
|
|
74
83
|
for message in messages:
|
|
75
84
|
num_tokens += self.param['tokens_per_message']
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TeLLMgramBot
|
|
3
|
-
Version: 2.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 2.4.0
|
|
4
|
+
Summary: LLM-powered Telegram bot (OpenAI + Anthropic)
|
|
5
5
|
Home-page: https://github.com/Digital-Heresy/TeLLMgramBot
|
|
6
6
|
Author: Digital Heresy
|
|
7
7
|
Author-email: ronin.atx@gmail.com
|
|
@@ -10,6 +10,7 @@ Requires-Python: >=3.12
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Requires-Dist: openai>2.0
|
|
13
|
+
Requires-Dist: anthropic>=0.40
|
|
13
14
|
Requires-Dist: PyYAML
|
|
14
15
|
Requires-Dist: httpx
|
|
15
16
|
Requires-Dist: beautifulsoup4
|
|
@@ -29,9 +30,9 @@ Dynamic: requires-python
|
|
|
29
30
|
Dynamic: summary
|
|
30
31
|
|
|
31
32
|
# TeLLMgramBot
|
|
32
|
-
The basic goal of this project is to create a bridge between a Telegram Bot and a Large
|
|
33
|
+
The basic goal of this project is to create a bridge between a Telegram Bot and a Large Language Model (LLM), supporting both OpenAI's GPT models and Anthropic's Claude models.
|
|
33
34
|
* To use this library, you must have a Telegram account **with a user name**, not just a phone number. If you don't have one, [create one online](https://telegram.org/).
|
|
34
|
-
* If added to a
|
|
35
|
+
* If added to a Telegram group, the bot must be [administrator](https://www.alphr.com/add-admin-telegram/) in order to respond to a user calling out its name, initials, or nickname.
|
|
35
36
|
<img src="assets/TeLLMgramBot_Logo.png" width=200 align=center />
|
|
36
37
|
|
|
37
38
|
## Telegram Bot + LLM Encapsulation
|
|
@@ -39,34 +40,45 @@ The basic goal of this project is to create a bridge between a Telegram Bot and
|
|
|
39
40
|
* The more dynamic conversation gets handed off to the LLM to manage prompts and responses, and Telegram acts as the interaction broker.
|
|
40
41
|
* Pass the URL in [square brackets] and mention how the bot should interpret it.
|
|
41
42
|
* Example: "What do you think of this article? [https://some_site/article]"
|
|
42
|
-
* This uses
|
|
43
|
+
* This uses a separate model (configurable via `url_model`) to support more URL content with its higher token limit.
|
|
43
44
|
* Tokens are used to measure the length of all conversation messages between the Telegram bot assistant and the user. This is useful to:
|
|
44
45
|
* Ensure the length does not go over the model limit. If it does, prune oldest messages to fit within the limit.
|
|
45
46
|
* Remember 50% of the past conversations when starting up TeLLMgramBot again.
|
|
46
47
|
* Users can also clear their conversation history for privacy.
|
|
47
48
|
|
|
48
49
|
## Why Telegram?
|
|
49
|
-
Using Telegram as the interface not only solves "exposing" the interface, but gives you
|
|
50
|
+
Using Telegram as the interface not only solves "exposing" the interface, but gives you boatloads of interactivity over a standard Command Line interface, or trying to create a website with input boxes and submit buttons to try to handle everything:
|
|
50
51
|
1. Telegram already lets you paste in verbose, multiline messages.
|
|
51
52
|
2. Telegram already lets you paste in pictures, videos, links, etc.
|
|
52
53
|
3. Telegram already lets you react with emojis, stickers, etc.
|
|
53
54
|
|
|
55
|
+
## Supported LLM Providers
|
|
56
|
+
TeLLMgramBot selects the LLM provider automatically based on the model name:
|
|
57
|
+
|
|
58
|
+
| Model prefix | Provider | Example models |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `gpt-` | OpenAI | `gpt-4o`, `gpt-4o-mini`, `gpt-5-mini` |
|
|
61
|
+
| `claude-` | Anthropic | `claude-sonnet-4-6`, `claude-haiku-4-5` |
|
|
62
|
+
|
|
63
|
+
Simply set `chat_model` (and optionally `url_model`) in your `config.yaml` to any supported model and supply the corresponding API key — no other changes needed.
|
|
64
|
+
|
|
54
65
|
## Directories
|
|
55
66
|
When initializing TeLLMgramBot, the following directories get created:
|
|
56
67
|
* `configs` - Contains bot configuration files.
|
|
57
68
|
* `config.yaml` (can be a different name)
|
|
58
|
-
* This file sets main
|
|
59
|
-
*
|
|
60
|
-
*
|
|
69
|
+
* This file sets main bot parameters like naming and the LLM models to use.
|
|
70
|
+
* `chat_model` — the model used for normal conversation (e.g. `gpt-5-mini` or `claude-sonnet-4-6`).
|
|
71
|
+
* `url_model` — the model used to read and summarize URL content, can differ from `chat_model`.
|
|
72
|
+
* An empty `token_limit` will use the maximum tokens supported by the `chat_model`.
|
|
61
73
|
* `tokenGPT.yaml`
|
|
62
|
-
*
|
|
63
|
-
*
|
|
74
|
+
* Contains token size parameters for all supported models.
|
|
75
|
+
* On first run, GPT and Claude model families are pre-populated. Additional models can be added manually.
|
|
64
76
|
* `prompts` - Contains prompt files for how the bot interacts with any user.
|
|
65
77
|
* `test_personality.prmpt` (can be a different name)
|
|
66
|
-
*
|
|
67
|
-
* The user can create more prompt files as needed for different personalities.
|
|
78
|
+
* A sample prompt file as a basis to test this library.
|
|
79
|
+
* The user can create more prompt files as needed for different personalities.
|
|
68
80
|
* `url_analysis.prmpt`
|
|
69
|
-
*
|
|
81
|
+
* Prompt template used to analyze URL content passed in brackets `[]`.
|
|
70
82
|
* `errorlogs`
|
|
71
83
|
* Contains a `tellmgrambot_error.log` file to investigate if there are problems during the interaction.
|
|
72
84
|
* User will also get notified to contact the owner.
|
|
@@ -84,24 +96,27 @@ TeLLMgramBot also creates or utilizes the following environment variables that c
|
|
|
84
96
|
If neither of these are defined, the initialization would use the top-level execution run directory.
|
|
85
97
|
|
|
86
98
|
## API Keys
|
|
87
|
-
To operate TeLLMgramBot,
|
|
88
|
-
* [OpenAI](https://platform.openai.com/overview)
|
|
89
|
-
* [
|
|
90
|
-
* [
|
|
99
|
+
To operate TeLLMgramBot, the following API keys are required:
|
|
100
|
+
* **[OpenAI](https://platform.openai.com/overview)** — required when using a `gpt-*` model.
|
|
101
|
+
* **[Anthropic](https://console.anthropic.com/)** — required when using a `claude-*` model.
|
|
102
|
+
* **[Telegram](https://core.telegram.org/api)** — always required; offers a Bot API through BotFather.
|
|
103
|
+
* **[VirusTotal](https://www.virustotal.com/gui/home/)** — always required; performs safety checks on URLs.
|
|
91
104
|
|
|
92
105
|
There are two ways to populate each API key: environment variables or `.key` files.
|
|
93
106
|
|
|
94
107
|
### Environment Variables
|
|
95
|
-
TeLLMgramBot uses the following environment variables
|
|
96
|
-
1. `TELLMGRAMBOT_OPENAI_API_KEY`
|
|
97
|
-
2. `
|
|
98
|
-
3. `
|
|
108
|
+
TeLLMgramBot uses the following environment variables for API keys:
|
|
109
|
+
1. `TELLMGRAMBOT_OPENAI_API_KEY` *(OpenAI models)*
|
|
110
|
+
2. `TELLMGRAMBOT_ANTHROPIC_API_KEY` *(Anthropic models)*
|
|
111
|
+
3. `TELLMGRAMBOT_TELEGRAM_API_KEY`
|
|
112
|
+
4. `TELLMGRAMBOT_VIRUSTOTAL_API_KEY`
|
|
99
113
|
|
|
100
114
|
During spin-up time, a user can call out `os.environ[env_var]` to set those variables, like the following example:
|
|
101
115
|
```
|
|
102
116
|
my_keys = Some_Vault_Fetch_Function()
|
|
103
117
|
|
|
104
|
-
os.environ['TELLMGRAMBOT_OPENAI_API_KEY'] = my_keys['
|
|
118
|
+
os.environ['TELLMGRAMBOT_OPENAI_API_KEY'] = my_keys['OpenAIKey']
|
|
119
|
+
os.environ['TELLMGRAMBOT_ANTHROPIC_API_KEY'] = my_keys['AnthropicKey']
|
|
105
120
|
os.environ['TELLMGRAMBOT_TELEGRAM_API_KEY'] = my_keys['BotFatherToken']
|
|
106
121
|
os.environ['TELLMGRAMBOT_VIRUSTOTAL_API_KEY'] = my_keys['VirusTotalToken']
|
|
107
122
|
```
|
|
@@ -109,15 +124,16 @@ os.environ['TELLMGRAMBOT_VIRUSTOTAL_API_KEY'] = my_keys['VirusTotalToken']
|
|
|
109
124
|
This means the user can implement whatever key vault they want to fetch the keys at runtime, without needing files stored in the directory.
|
|
110
125
|
|
|
111
126
|
### API Key Files
|
|
112
|
-
The other route is to create
|
|
127
|
+
The other route is to create files by the base path during execution or a specified environment variable `TELLMGRAMBOT_KEYS_PATH`. By default, files are created for the user to input each API key:
|
|
113
128
|
1. `openai.key`
|
|
114
|
-
2. `
|
|
115
|
-
3. `
|
|
129
|
+
2. `anthropic.key` _(planned — env var only for now; see Phase 3)_
|
|
130
|
+
3. `telegram.key`
|
|
131
|
+
4. `virustotal.key`
|
|
116
132
|
|
|
117
133
|
Each file with the associated API key will update its respective environment variable if not defined.
|
|
118
134
|
|
|
119
135
|
## Bot Setup
|
|
120
|
-
This library includes an example script `test_local.py`, which uses files from the folders `configs` and `prompts` for TeLLMgramBot to process.
|
|
136
|
+
This library includes an example script `test_local.py`, which uses files from the folders `configs` and `prompts` for TeLLMgramBot to process.
|
|
121
137
|
1. Ensure the previous sections are followed with the proper API keys and your Telegram bot set.
|
|
122
138
|
2. Install this library via PIP (`pip install TeLLMgramBot`) and then import into your project.
|
|
123
139
|
3. Instantiate the bot by passing in various configuration pieces needed below.
|
|
@@ -127,8 +143,8 @@ This library includes an example script `test_local.py`, which uses files from t
|
|
|
127
143
|
bot_owner = <Bot owner's Telegram username>,
|
|
128
144
|
bot_nickname = <Bot nickname like 'Botty'>,
|
|
129
145
|
bot_initials = <Bot initials like 'FB'>,
|
|
130
|
-
chat_model = <Conversation model like 'gpt-4o-mini'>,
|
|
131
|
-
url_model = <URL analysis model like 'gpt-4o'>,
|
|
146
|
+
chat_model = <Conversation model like 'gpt-4o-mini' or 'claude-sonnet-4-6'>,
|
|
147
|
+
url_model = <URL analysis model like 'gpt-4o' or 'claude-haiku-4-5'>,
|
|
132
148
|
token_limit = <Maximum token count set, by default chat_model max>,
|
|
133
149
|
persona_temp = <LLM factual to creative value [0-2], by default 1.0>,
|
|
134
150
|
persona_prompt = <System prompt summarizing bot personality>
|
|
@@ -143,9 +159,11 @@ This library includes an example script `test_local.py`, which uses files from t
|
|
|
143
159
|
|
|
144
160
|
## Resources
|
|
145
161
|
* GitHub repository [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) has guides to create a Telegram bot.
|
|
146
|
-
* For more information on OpenAI models
|
|
147
|
-
* [OpenAI model overview and maximum tokens](https://platform.openai.com/docs/models)
|
|
148
|
-
* [OpenAI message conversion to tokens](https://github.com/openai/openai-python)
|
|
149
|
-
* [OpenAI custom fine-tuning](https://platform.openai.com/docs/guides/model-optimization)
|
|
150
|
-
* [OpenAI's tiktoken library
|
|
151
|
-
*
|
|
162
|
+
* For more information on OpenAI models and token limits:
|
|
163
|
+
* [OpenAI model overview and maximum tokens](https://platform.openai.com/docs/models)
|
|
164
|
+
* [OpenAI message conversion to tokens](https://github.com/openai/openai-python)
|
|
165
|
+
* [OpenAI custom fine-tuning](https://platform.openai.com/docs/guides/model-optimization)
|
|
166
|
+
* [OpenAI's tiktoken library](https://github.com/openai/tiktoken/tree/main)
|
|
167
|
+
* For more information on Anthropic Claude models:
|
|
168
|
+
* [Anthropic model overview and context windows](https://docs.anthropic.com/en/docs/about-claude/models)
|
|
169
|
+
* [Anthropic Python SDK](https://github.com/anthropic/anthropic-sdk-python)
|
|
@@ -6,7 +6,6 @@ TeLLMgramBot/__init__.py
|
|
|
6
6
|
TeLLMgramBot/conversation.py
|
|
7
7
|
TeLLMgramBot/initialize.py
|
|
8
8
|
TeLLMgramBot/message_handlers.py
|
|
9
|
-
TeLLMgramBot/openai_singleton.py
|
|
10
9
|
TeLLMgramBot/tokenGPT.py
|
|
11
10
|
TeLLMgramBot/utils.py
|
|
12
11
|
TeLLMgramBot/web_utils.py
|
|
@@ -14,4 +13,9 @@ TeLLMgramBot.egg-info/PKG-INFO
|
|
|
14
13
|
TeLLMgramBot.egg-info/SOURCES.txt
|
|
15
14
|
TeLLMgramBot.egg-info/dependency_links.txt
|
|
16
15
|
TeLLMgramBot.egg-info/requires.txt
|
|
17
|
-
TeLLMgramBot.egg-info/top_level.txt
|
|
16
|
+
TeLLMgramBot.egg-info/top_level.txt
|
|
17
|
+
TeLLMgramBot/providers/__init__.py
|
|
18
|
+
TeLLMgramBot/providers/anthropic_provider.py
|
|
19
|
+
TeLLMgramBot/providers/base.py
|
|
20
|
+
TeLLMgramBot/providers/factory.py
|
|
21
|
+
TeLLMgramBot/providers/openai_provider.py
|
|
@@ -5,17 +5,18 @@ with open("README.md", "r") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name='TeLLMgramBot',
|
|
8
|
-
version='2.
|
|
8
|
+
version='2.4.0',
|
|
9
9
|
packages=find_packages(),
|
|
10
10
|
license='MIT',
|
|
11
11
|
author='Digital Heresy',
|
|
12
12
|
author_email='ronin.atx@gmail.com',
|
|
13
|
-
description='
|
|
13
|
+
description='LLM-powered Telegram bot (OpenAI + Anthropic)',
|
|
14
14
|
long_description=long_description,
|
|
15
15
|
long_description_content_type="text/markdown",
|
|
16
16
|
url='https://github.com/Digital-Heresy/TeLLMgramBot',
|
|
17
17
|
install_requires=[
|
|
18
18
|
'openai>2.0',
|
|
19
|
+
'anthropic>=0.40',
|
|
19
20
|
'PyYAML',
|
|
20
21
|
'httpx',
|
|
21
22
|
'beautifulsoup4',
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from openai import AsyncOpenAI
|
|
3
|
-
|
|
4
|
-
class OpenAIClientSingleton:
|
|
5
|
-
"""Call the OpenAI Client API for processing every asynchronous function."""
|
|
6
|
-
_instance = None
|
|
7
|
-
|
|
8
|
-
@classmethod
|
|
9
|
-
def get_instance(cls):
|
|
10
|
-
if cls._instance is None:
|
|
11
|
-
cls._instance = AsyncOpenAI(api_key=os.environ['TELLMGRAMBOT_OPENAI_API_KEY'])
|
|
12
|
-
return cls._instance
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|