sopel-ai 1.0.14__py3-none-any.whl → 1.3.4__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- sopel_ai/__init__.py +4 -1
- sopel_ai/config.py +2 -0
- sopel_ai/core.py +63 -24
- sopel_ai/errors.py +1 -1
- sopel_ai/plugin.py +26 -15
- {sopel_ai-1.0.14.dist-info → sopel_ai-1.3.4.dist-info}/METADATA +72 -10
- sopel_ai-1.3.4.dist-info/RECORD +11 -0
- sopel_ai-1.0.14.dist-info/RECORD +0 -11
- {sopel_ai-1.0.14.dist-info → sopel_ai-1.3.4.dist-info}/LICENSE.txt +0 -0
- {sopel_ai-1.0.14.dist-info → sopel_ai-1.3.4.dist-info}/WHEEL +0 -0
- {sopel_ai-1.0.14.dist-info → sopel_ai-1.3.4.dist-info}/entry_points.txt +0 -0
- {sopel_ai-1.0.14.dist-info → sopel_ai-1.3.4.dist-info}/top_level.txt +0 -0
sopel_ai/__init__.py
CHANGED
sopel_ai/config.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# See: https://raw.githubcontent.com/pr3d4t0r/sopel_ai/master/LICENSE.txt
|
2
2
|
|
3
3
|
from sopel import config
|
4
|
+
from sopel_ai.core import DEFAULT_API_KEY
|
4
5
|
from sopel_ai.core import DEFAULT_LLM
|
5
6
|
from sopel_ai.core import DEFAULT_LLM_PROVIDER
|
6
7
|
from sopel_ai.core import DEFAULT_LLM_SERVICE
|
@@ -9,6 +10,7 @@ from sopel_ai.core import DEFAULT_LOG_LEVEL
|
|
9
10
|
|
10
11
|
class SopelAISection(config.types.StaticSection):
|
11
12
|
llm_engine = config.types.ValidatedAttribute('llm_engine', str, default = DEFAULT_LLM)
|
13
|
+
llm_key = config.types.ValidatedAttribute('llm_key', str, default = DEFAULT_API_KEY, is_secret = True)
|
12
14
|
llm_provider = config.types.ValidatedAttribute('llm_provider', str, default = DEFAULT_LLM_PROVIDER)
|
13
15
|
llm_service = config.types.ValidatedAttribute('llm_service', str, default = DEFAULT_LLM_SERVICE)
|
14
16
|
logLevel = config.types.ValidatedAttribute('logLevel', str, default = DEFAULT_LOG_LEVEL)
|
sopel_ai/core.py
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
# See: https://raw.githubcontent.com/pr3d4t0r/sopel_ai/master/LICENSE.txt
|
2
2
|
|
3
|
-
from perplexipy import PERPLEXITY_API_KEY
|
4
3
|
from perplexipy import PERPLEXITY_API_URL
|
5
4
|
from perplexipy import PERPLEXITY_DEFAULT_MODEL
|
6
5
|
from perplexipy import PerplexityClient
|
7
6
|
from sopel_ai import __VERSION__
|
8
|
-
from sopel_ai.errors import
|
7
|
+
from sopel_ai.errors import SopelAIError
|
9
8
|
from tinydb import Query
|
10
9
|
from tinydb import TinyDB
|
11
10
|
|
@@ -14,6 +13,7 @@ from tinydb import TinyDB
|
|
14
13
|
|
15
14
|
# +++ constants +++
|
16
15
|
|
16
|
+
DEFAULT_API_KEY = 'pplx-3a45enterthekeyhere'
|
17
17
|
DEFAULT_LLM = PERPLEXITY_DEFAULT_MODEL
|
18
18
|
DEFAULT_LLM_PROVIDER = 'PerplexityAI'
|
19
19
|
DEFAULT_LLM_SERVICE = PERPLEXITY_API_URL
|
@@ -54,19 +54,20 @@ def _checkDB(fileName: str) -> TinyDB:
|
|
54
54
|
|
55
55
|
if not _database:
|
56
56
|
_database = TinyDB(fileName)
|
57
|
+
_database.table('_default', cache_size = 0)
|
57
58
|
|
58
59
|
return _database
|
59
60
|
|
60
61
|
|
61
|
-
def _checkClientInstance() -> None:
|
62
|
+
def _checkClientInstance(key: str) -> None:
|
62
63
|
global _client
|
63
64
|
|
64
65
|
if not _client:
|
65
|
-
_client = PerplexityClient(key =
|
66
|
+
_client = PerplexityClient(key = key, endpoint = PERPLEXITY_API_URL)
|
66
67
|
_client.model = PERPLEXITY_DEFAULT_MODEL
|
67
68
|
|
68
69
|
|
69
|
-
def runQuery(query: str, nick: str = None, fileNameDB: str = None, responseLength: int = MAX_RESPONSE_LENGTH) -> str:
|
70
|
+
def runQuery(query: str, nick: str = None, fileNameDB: str = None, responseLength: int = MAX_RESPONSE_LENGTH, key: str = None) -> str:
|
70
71
|
"""
|
71
72
|
Run a query against the LLM engine using the PerplexipyClient, and return the
|
72
73
|
query result in a string.
|
@@ -87,25 +88,36 @@ def runQuery(query: str, nick: str = None, fileNameDB: str = None, responseLengt
|
|
87
88
|
responseLength
|
88
89
|
The maximum response length requested from the AI provider. See `MAX_RESPONSE_LENGTH`.
|
89
90
|
|
91
|
+
key
|
92
|
+
The LLM service provider API key.
|
93
|
+
|
90
94
|
Returns
|
91
95
|
-------
|
92
96
|
A string with the response if the service found a reasonable and convenient
|
93
97
|
one, or the text of an Error and the possible cause, as reported by the
|
94
98
|
Python run-time.
|
95
99
|
|
100
|
+
Raises
|
101
|
+
------
|
102
|
+
`SopelAIError` if the `key` is empty or if the query is invalid. The string
|
103
|
+
message in the error reflects the cause.
|
104
|
+
|
96
105
|
---
|
97
106
|
"""
|
107
|
+
if not key:
|
108
|
+
raise SopelAIError('key argument cannot be empty - set the LLM service API key')
|
109
|
+
|
98
110
|
_checkDB(fileNameDB)
|
99
|
-
model = getModelForUser(nick, fileNameDB)
|
111
|
+
model = getModelForUser(nick, fileNameDB, key)
|
100
112
|
if not nick or model == DEFAULT_LLM:
|
101
|
-
_checkClientInstance()
|
113
|
+
_checkClientInstance(key)
|
102
114
|
client = _client
|
103
115
|
else:
|
104
116
|
client = _clientCache[nick]
|
105
117
|
|
106
118
|
try:
|
107
119
|
if not query:
|
108
|
-
raise
|
120
|
+
raise SopelAIError('query parameter cannot be empty')
|
109
121
|
query = 'Brief answer in %s characters or less to: "%s". Include one URL in the response and strip off all Markdown and hashtags.' % (responseLength, query)
|
110
122
|
result = client.query(query).replace('\n', '')
|
111
123
|
except Exception as e:
|
@@ -114,7 +126,7 @@ def runQuery(query: str, nick: str = None, fileNameDB: str = None, responseLengt
|
|
114
126
|
return result
|
115
127
|
|
116
128
|
|
117
|
-
def modelsList() -> list:
|
129
|
+
def modelsList(key: str = None) -> list:
|
118
130
|
"""
|
119
131
|
Returns a list of all available models so that they can be used for
|
120
132
|
requesting a specific one in another command.
|
@@ -125,26 +137,37 @@ def modelsList() -> list:
|
|
125
137
|
order depends on what the underlying API reports, and it's unlikely to
|
126
138
|
change between calls.
|
127
139
|
|
128
|
-
Other
|
140
|
+
Other SopelAI functions will use the index to refer to a model in the
|
129
141
|
collection.
|
130
142
|
|
143
|
+
Raises
|
144
|
+
------
|
145
|
+
`SopelAIError` if the `key` is empty or if the query is invalid. The string
|
146
|
+
message in the error reflects the cause.
|
147
|
+
|
131
148
|
---
|
132
149
|
"""
|
133
|
-
|
150
|
+
if not key:
|
151
|
+
raise SopelAIError('key argument cannot be empty - set the LLM service API key')
|
152
|
+
|
153
|
+
_checkClientInstance(key)
|
134
154
|
|
135
155
|
return sorted(list(_client.models.keys()))
|
136
156
|
|
137
157
|
|
138
|
-
def versionInfo() -> str:
|
139
|
-
|
158
|
+
def versionInfo(key: str = None) -> str:
|
159
|
+
if not key:
|
160
|
+
raise SopelAIError('key argument cannot be empty - set the LLM service API key')
|
161
|
+
|
162
|
+
_checkClientInstance(key)
|
140
163
|
return 'sopel_ai v%s using %s' % (__VERSION__, '.'.join([_client.__class__.__module__, _client.__class__.__name__]))
|
141
164
|
|
142
165
|
|
143
|
-
def setModelForUser(modelID: int, nick: str, fileNameDB: str) -> str:
|
166
|
+
def setModelForUser(modelID: int, nick: str, fileNameDB: str, key = None) -> str:
|
144
167
|
"""
|
145
168
|
Set the model associated with `modelID` for processing requests from `nick`.
|
146
169
|
The `modelID` is the index into the `models` object returned by
|
147
|
-
`
|
170
|
+
`sopel_ai.modelsList()`, from zero.
|
148
171
|
|
149
172
|
Arguments
|
150
173
|
---------
|
@@ -158,6 +181,9 @@ def setModelForUser(modelID: int, nick: str, fileNameDB: str) -> str:
|
|
158
181
|
fileNameDB
|
159
182
|
The path to the database in the file system. Can be absolute or relative.
|
160
183
|
|
184
|
+
key
|
185
|
+
The LLM service provider API key.
|
186
|
+
|
161
187
|
The function assumes that `nick` represents a valid user /nick because Sopel
|
162
188
|
enforces that the exists and is registered in the server.
|
163
189
|
|
@@ -167,26 +193,26 @@ def setModelForUser(modelID: int, nick: str, fileNameDB: str) -> str:
|
|
167
193
|
|
168
194
|
Raises
|
169
195
|
------
|
170
|
-
`
|
196
|
+
`sopel_ai.errors.SopelAIError` if the arguments are invalid or out of range.
|
171
197
|
|
172
198
|
---
|
173
199
|
"""
|
174
200
|
_checkDB(fileNameDB)
|
175
|
-
models= modelsList()
|
201
|
+
models= modelsList(key)
|
176
202
|
if modelID not in range(len(models)):
|
177
|
-
raise
|
203
|
+
raise SopelAIError('modelID outside of available models index range')
|
178
204
|
|
179
|
-
|
205
|
+
query = Query()
|
180
206
|
|
181
|
-
if _database.search(
|
182
|
-
_database.update({ 'model': models[modelID], },
|
207
|
+
if _database.search(query.nick == nick):
|
208
|
+
_database.update({ 'model': models[modelID], }, query.nick == nick)
|
183
209
|
else:
|
184
210
|
_database.insert({ 'nick': nick, 'model': models[modelID], })
|
185
211
|
|
186
212
|
return models[modelID]
|
187
213
|
|
188
214
|
|
189
|
-
def getModelForUser(nick: str, fileNameDB: str) -> str:
|
215
|
+
def getModelForUser(nick: str, fileNameDB: str, key = None) -> str:
|
190
216
|
"""
|
191
217
|
Get the model name for the user with `nick`.
|
192
218
|
|
@@ -198,19 +224,32 @@ def getModelForUser(nick: str, fileNameDB: str) -> str:
|
|
198
224
|
fileNameDB
|
199
225
|
The path to the database in the file system. Can be absolute or relative.
|
200
226
|
|
227
|
+
key
|
228
|
+
The LLM service provider API key.
|
229
|
+
|
201
230
|
Returns
|
202
231
|
-------
|
203
232
|
A string representing the model name, if one exists in the database
|
204
|
-
associated with the user, `
|
233
|
+
associated with the user, `sopel_ai.DEFAULT_LLM` otherwise.
|
234
|
+
|
235
|
+
Raises
|
236
|
+
------
|
237
|
+
`sopel_ai.errors.SopelAIError` if the arguments are invalid or out of range.
|
205
238
|
|
206
239
|
---
|
207
240
|
"""
|
241
|
+
if not key:
|
242
|
+
raise SopelAIError('key argument cannot be empty - set the LLM service API key')
|
243
|
+
|
208
244
|
_checkDB(fileNameDB)
|
209
245
|
Preference = Query()
|
210
246
|
preference = _database.search(Preference.nick == nick)
|
211
247
|
if preference:
|
248
|
+
client = PerplexityClient(key = key, endpoint = PERPLEXITY_API_URL)
|
212
249
|
model = preference[0]['model']
|
213
|
-
|
250
|
+
# TODO: Implement unit test for this case.
|
251
|
+
if model not in client.models.keys():
|
252
|
+
model = tuple(client.models.keys())[0]
|
214
253
|
client.model = model
|
215
254
|
_clientCache[nick] = client
|
216
255
|
return model
|
sopel_ai/errors.py
CHANGED
sopel_ai/plugin.py
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
# See: https://raw.githubcontent.com/pr3d4t0r/sopel_ai/master/LICENSE.txt
|
2
2
|
"""
|
3
|
-
|
4
|
-
user-callable objects, functions defined in it. If in doubt, user the Force and
|
5
|
-
read the Source.
|
3
|
+
Sopel AI interactive models service
|
6
4
|
"""
|
7
5
|
|
6
|
+
from sopel import formatting
|
7
|
+
from sopel import plugin
|
8
8
|
from sopel.bot import Sopel
|
9
9
|
from sopel.bot import SopelWrapper
|
10
10
|
from sopel.config import Config
|
11
|
-
from sopel import formatting
|
12
|
-
from sopel import plugin
|
13
11
|
from sopel.trigger import Trigger
|
14
12
|
from sopel_ai.config import SopelAISection
|
13
|
+
from sopel_ai.core import DEFAULT_API_KEY
|
15
14
|
from sopel_ai.core import DEFAULT_LLM
|
16
15
|
from sopel_ai.core import DEFAULT_LLM_PROVIDER
|
17
16
|
from sopel_ai.core import DEFAULT_LLM_SERVICE
|
@@ -26,6 +25,12 @@ from sopel_ai.core import versionInfo
|
|
26
25
|
|
27
26
|
import os
|
28
27
|
|
28
|
+
"""
|
29
|
+
This module is intended for interfacing with Sopel and there are no
|
30
|
+
user-callable objects, functions defined in it. If in doubt, user the Force and
|
31
|
+
read the Source.
|
32
|
+
"""
|
33
|
+
|
29
34
|
|
30
35
|
# +++ constants +++
|
31
36
|
|
@@ -49,6 +54,7 @@ def configure(config: Config) -> None:
|
|
49
54
|
"""
|
50
55
|
config.define_section('sopel_ai', SopelAISection)
|
51
56
|
config.sopel_ai.configure_setting('llm_engine', 'Set the LLM engine', default = DEFAULT_LLM)
|
57
|
+
config.sopel_ai.configure_setting('llm_key', 'Set the API key', default = DEFAULT_API_KEY)
|
52
58
|
config.sopel_ai.configure_setting('llm_provider', 'Set the LLM provider name', default = DEFAULT_LLM_PROVIDER)
|
53
59
|
config.sopel_ai.configure_setting('llm_service', 'Set the LLM service URL', default = DEFAULT_LLM_SERVICE)
|
54
60
|
config.sopel_ai.configure_setting('logLevel', 'Set the log level', default = DEFAULT_LOG_LEVEL)
|
@@ -66,7 +72,7 @@ def _queryCommand(bot: SopelWrapper, trigger: Trigger) -> None:
|
|
66
72
|
return
|
67
73
|
|
68
74
|
# TODO: Log this
|
69
|
-
bot.reply(runQuery(trigger.group(2), trigger.nick, fileNameDB = _USER_DB_FILE))
|
75
|
+
bot.reply(runQuery(trigger.group(2), trigger.nick, fileNameDB = _USER_DB_FILE, key = bot.config.sopel_ai.llm_key))
|
70
76
|
|
71
77
|
|
72
78
|
@plugin.commands('qpm', 'llmqpm')
|
@@ -80,7 +86,12 @@ def _queryCommandPrivateMessage(bot: SopelWrapper, trigger: Trigger) -> None:
|
|
80
86
|
bot.reply('No search term. Usage: {}qpm Some question about anything'.format(bot.config.core.help_prefix))
|
81
87
|
return
|
82
88
|
|
83
|
-
bot.say(runQuery(
|
89
|
+
bot.say(runQuery(
|
90
|
+
trigger.group(2),
|
91
|
+
trigger.nick,
|
92
|
+
fileNameDB = _USER_DB_FILE,
|
93
|
+
responseLength = _PRIVATE_MESSAGE_RESPONSE_LENGTH,
|
94
|
+
key = bot.config.sopel_ai.llm_key), trigger.nick)
|
84
95
|
|
85
96
|
|
86
97
|
@plugin.commands('mver')
|
@@ -89,7 +100,7 @@ def _queryCommandPrivateMessage(bot: SopelWrapper, trigger: Trigger) -> None:
|
|
89
100
|
@plugin.require_account(message = 'You must be a registered to use this command.', reply = True)
|
90
101
|
@plugin.thread(True)
|
91
102
|
def _versionCommand(bot: SopelWrapper, trigger: Trigger) -> None:
|
92
|
-
bot.reply(versionInfo())
|
103
|
+
bot.reply(versionInfo(key = bot.config.sopel_ai.llm_key))
|
93
104
|
|
94
105
|
|
95
106
|
@plugin.commands('models')
|
@@ -98,7 +109,7 @@ def _versionCommand(bot: SopelWrapper, trigger: Trigger) -> None:
|
|
98
109
|
@plugin.require_account(message = 'You must be a registered to use this command.', reply = True)
|
99
110
|
@plugin.thread(True)
|
100
111
|
def _modelsCommand(bot: SopelWrapper, trigger: Trigger) -> None:
|
101
|
-
models = sorted(modelsList())
|
112
|
+
models = sorted(modelsList(key = bot.config.sopel_ai.llm_key))
|
102
113
|
s = ''
|
103
114
|
for index in range(len(models)):
|
104
115
|
s += '[%d] %s; ' % (index+1, models[index])
|
@@ -115,13 +126,13 @@ def _setModelCommand(bot: SopelWrapper, trigger: Trigger) -> None:
|
|
115
126
|
modelID = int(trigger.group(2))
|
116
127
|
except:
|
117
128
|
modelID = -1
|
118
|
-
models = modelsList()
|
129
|
+
models = modelsList(bot.config.sopel_ai.llm_key)
|
119
130
|
if modelID not in range(1, len(models)+1):
|
120
131
|
message = 'Invalid model ID; must be in range %s. Usage: {}setmodel n, where n ::= integer' % ('1 - %d' % len(models))
|
121
132
|
bot.reply(message.format(bot.config.core.help_prefix))
|
122
133
|
else:
|
123
134
|
effectiveModelID = modelID-1
|
124
|
-
effectiveModel = setModelForUser(effectiveModelID, trigger.nick, _USER_DB_FILE)
|
135
|
+
effectiveModel = setModelForUser(effectiveModelID, trigger.nick, _USER_DB_FILE, key = bot.config.sopel_ai.llm_key)
|
125
136
|
bot.reply('All your future interactions will use the %s model.' % effectiveModel)
|
126
137
|
|
127
138
|
|
@@ -131,7 +142,7 @@ def _setModelCommand(bot: SopelWrapper, trigger: Trigger) -> None:
|
|
131
142
|
@plugin.require_account(message = 'You must be a registered to use this command.', reply = True)
|
132
143
|
@plugin.thread(True)
|
133
144
|
def _getModelCommand(bot: SopelWrapper, trigger: Trigger) -> None:
|
134
|
-
bot.reply(getModelForUser(trigger.nick, _USER_DB_FILE))
|
145
|
+
bot.reply(getModelForUser(trigger.nick, _USER_DB_FILE, key = bot.config.sopel_ai.llm_key))
|
135
146
|
|
136
147
|
|
137
148
|
@plugin.commands('mymodel')
|
@@ -146,12 +157,12 @@ def _myModelCommand(bot: SopelWrapper, trigger: Trigger) -> None:
|
|
146
157
|
_setModelCommand(bot, trigger)
|
147
158
|
|
148
159
|
|
149
|
-
@plugin.commands('
|
150
|
-
@plugin.example('.
|
160
|
+
@plugin.commands('reqai')
|
161
|
+
@plugin.example('.reqai Displays the URL for opening a GitHub features/bug/issues request for sopel_ai')
|
151
162
|
@plugin.output_prefix(_PLUGIN_OUTPUT_PREFIX)
|
152
163
|
@plugin.require_account(message = 'You must be a registered to use this command.', reply = True)
|
153
164
|
@plugin.thread(True)
|
154
165
|
def _reqCommand(bot: SopelWrapper, trigger: Trigger) -> None:
|
155
166
|
locator = formatting.bold(GITHUB_NEW_ISSUE_URL)
|
156
|
-
bot.reply('SopelAI version %s. Enter your
|
167
|
+
bot.reply('SopelAI version %s. Enter your feature request or bug report at this URL: %s' % (__VERSION__, locator))
|
157
168
|
|
@@ -1,8 +1,9 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sopel-ai
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.3.4
|
4
4
|
Summary: Sopel AI - an LLM enhanced chat bot plug-in
|
5
5
|
Author-email: The SopelAI team <sopel_ai@cime.net>
|
6
|
+
License: BSD-3-Clause
|
6
7
|
Project-URL: Homepage, https://github.com/pr3d4t0r/sopel_ai
|
7
8
|
Project-URL: Bug Tracker, https://github.com/pr3d4t0r/sopel_ai/issues
|
8
9
|
Keywords: ai,bot,irc,llm,plugin,sopel
|
@@ -19,11 +20,11 @@ Classifier: Topic :: Utilities
|
|
19
20
|
Requires-Python: >=3.9
|
20
21
|
Description-Content-Type: text/markdown
|
21
22
|
License-File: LICENSE.txt
|
22
|
-
Requires-Dist: perplexipy
|
23
|
+
Requires-Dist: perplexipy (==1.1.4)
|
23
24
|
Requires-Dist: sopel (>=7.1)
|
24
25
|
Requires-Dist: tinydb
|
25
26
|
|
26
|
-
% sopel_ai(1) Version 1.
|
27
|
+
% sopel_ai(1) Version 1.3.4 chatbot plugin
|
27
28
|
|
28
29
|
Name
|
29
30
|
====
|
@@ -105,8 +106,8 @@ The bot produces a numbered list of supported models by issuing:
|
|
105
106
|
`.models`
|
106
107
|
|
107
108
|
Users are welcome to change the default model to one of those listed by issuing
|
108
|
-
the `.mymodel` command followed by the item number for the desired model from
|
109
|
-
list:
|
109
|
+
the `.mymodel` command followed by the item number for the desired model from
|
110
|
+
the list:
|
110
111
|
|
111
112
|
`.mymodel 1`
|
112
113
|
|
@@ -114,6 +115,15 @@ Users may request private instead of in-channel responses:
|
|
114
115
|
|
115
116
|
`.qpm Quote the Three Laws of Robotics and give me examples.`
|
116
117
|
|
118
|
+
Responses generated by making `.q` queries are expected to be short or are
|
119
|
+
trunked at 480 characters. They are intended to appear in-channel and to be as
|
120
|
+
brief as possible.
|
121
|
+
|
122
|
+
Responses generated from a `.qpm` query are expected to be long and detailed,
|
123
|
+
with a 16 KB length limit, span multpile messages (due to ircv3 limitations),
|
124
|
+
and `sopel_ai` presents them to the user in private message, regardless of
|
125
|
+
whether they were issued from a channel or a direct message.
|
126
|
+
|
117
127
|
Users can query the bot plugin and AI provider using:
|
118
128
|
|
119
129
|
`.mver`
|
@@ -127,13 +137,65 @@ support other providers.
|
|
127
137
|
|
128
138
|
API Key
|
129
139
|
=======
|
130
|
-
All AI services providers require an API key for access.
|
131
|
-
|
140
|
+
All AI services providers require an API key for access. The API key is
|
141
|
+
configured via:
|
142
|
+
|
143
|
+
`sopel config`
|
144
|
+
|
145
|
+
Or edit this section in the Sopel configuration file:
|
146
|
+
|
147
|
+
```ini
|
148
|
+
[sopel_ai]
|
149
|
+
.
|
150
|
+
.
|
151
|
+
llm_key = pplx-3a45enteryourkeykere
|
152
|
+
```
|
153
|
+
|
154
|
+
|
155
|
+
Docker
|
156
|
+
======
|
157
|
+
Sopel AI is dockerized and available from Docker Hub as pr3d4t0r/sopel_ai. The
|
158
|
+
version tag is the same as the latest version number for Sopel AI.
|
159
|
+
|
160
|
+
The examples in this section assume execution from the local file system. Adapt
|
161
|
+
as needed to run in a Kubernets cluster or other deployment method.
|
162
|
+
|
163
|
+
|
164
|
+
### First time
|
132
165
|
|
133
|
-
|
166
|
+
The Sopel + AI configuration file must be created:
|
167
|
+
|
168
|
+
```bash
|
169
|
+
docker run -ti -v ${HOME}/sopel_ai_data:/home/sopel_ai \
|
170
|
+
pr3d4t0r/sopel_ai:latest \
|
171
|
+
sopel configure
|
172
|
+
```
|
173
|
+
|
174
|
+
The API key and other relevant configuration data must be provided at this time.
|
175
|
+
`$HOME/sopel_ai_data` is volume mapped to the container's `/home/sopel_ai/.sopel
|
176
|
+
directory. Ensure that your host has write permissions in the shared volume.
|
177
|
+
|
178
|
+
The `pr3d4t0r/sopel_ai:latest` image is used if no version is specified. The
|
179
|
+
image update policy is left to the sysops and is not automatic.
|
180
|
+
|
181
|
+
Once `$HOME/sopel_ai_data` exists it's possible to copy the contents of a
|
182
|
+
different `~/.sopel` directory to it and use is as the configuration and Sopel
|
183
|
+
AI database store.
|
184
|
+
|
185
|
+
|
186
|
+
### Starting Sopel AI
|
187
|
+
|
188
|
+
A Docker Compose file is provided as an example of how to start the service,
|
189
|
+
<a href='./dockerized/docker-compose.yaml' target='_blank'>docker-file.yaml</a>. With this Docker Compose
|
190
|
+
file in the current directory, start the service with:
|
191
|
+
|
192
|
+
```bash
|
193
|
+
docker-compose up [-d] sopel_ai
|
194
|
+
|
195
|
+
```
|
134
196
|
|
135
|
-
|
136
|
-
|
197
|
+
The `-d` parameter daemonizes the service. Without it, the service will start
|
198
|
+
and display its output in the current console.
|
137
199
|
|
138
200
|
|
139
201
|
License
|
@@ -0,0 +1,11 @@
|
|
1
|
+
sopel_ai/__init__.py,sha256=UXSuXZj_p860rLAiewSe5nbKg-qbIEcXq6jkqYmaMnI,2531
|
2
|
+
sopel_ai/config.py,sha256=y0vbvfeDjjfLUOvTJ3W56mAGVpJxeizrXJbCaFW4ZXk,867
|
3
|
+
sopel_ai/core.py,sha256=m5XutY1Rm7z-nh1_qPOAcrnfP20BekMH6jveXoSX3NQ,7425
|
4
|
+
sopel_ai/errors.py,sha256=XMVgFk4Rw64Z0UO3ZInp-N6LP0GRG8NFIuXKnhu5rLo,192
|
5
|
+
sopel_ai/plugin.py,sha256=51mYp6B_mUtgS6mL-IMhK8Qiz_Rw_qAwmTLdML-qQ2o,6800
|
6
|
+
sopel_ai-1.3.4.dist-info/LICENSE.txt,sha256=I8aHapysmbM9F3y-rUfp011GQoosNO5L8pzl7IKgPnE,1531
|
7
|
+
sopel_ai-1.3.4.dist-info/METADATA,sha256=yUU8251bdLtOwpudjsUOZLdjAZ9DeSrsdmXVVXLVx98,6210
|
8
|
+
sopel_ai-1.3.4.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
9
|
+
sopel_ai-1.3.4.dist-info/entry_points.txt,sha256=7Juxcn6L4j6F83TjkviiTwiyXLM4gZxAAXFQDR2G_m4,43
|
10
|
+
sopel_ai-1.3.4.dist-info/top_level.txt,sha256=kpNMzNEGbhCXkyn7oc3uQPmrX1J6qLxn59IcZBpwSYg,9
|
11
|
+
sopel_ai-1.3.4.dist-info/RECORD,,
|
sopel_ai-1.0.14.dist-info/RECORD
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
sopel_ai/__init__.py,sha256=qH1q1GP7whFC-A28pYlcg2YIb9WGv7TFnUykJqXp6Lc,2473
|
2
|
-
sopel_ai/config.py,sha256=Kj8yacVbjjC4FRz7ser16Hf4fS9_XwvXK3f5dPSpZ0I,718
|
3
|
-
sopel_ai/core.py,sha256=Jy7zyzc_mbvs_9nUeetH-hlAAus087zfXW9_h3Vsv4Q,6181
|
4
|
-
sopel_ai/errors.py,sha256=fgc9mjYJMnq1AxFXOwiknFwnJBJTWRNkZIHzRKS9m5g,191
|
5
|
-
sopel_ai/plugin.py,sha256=tCsTsqQsuBC4orcEZCQu2d2iS09NvVPBpook0fV7w5k,6306
|
6
|
-
sopel_ai-1.0.14.dist-info/LICENSE.txt,sha256=I8aHapysmbM9F3y-rUfp011GQoosNO5L8pzl7IKgPnE,1531
|
7
|
-
sopel_ai-1.0.14.dist-info/METADATA,sha256=_OcL-mDwQVViDahpFK7gcI7fZMnaKuiTWUYg-f62ENo,4324
|
8
|
-
sopel_ai-1.0.14.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
9
|
-
sopel_ai-1.0.14.dist-info/entry_points.txt,sha256=7Juxcn6L4j6F83TjkviiTwiyXLM4gZxAAXFQDR2G_m4,43
|
10
|
-
sopel_ai-1.0.14.dist-info/top_level.txt,sha256=kpNMzNEGbhCXkyn7oc3uQPmrX1J6qLxn59IcZBpwSYg,9
|
11
|
-
sopel_ai-1.0.14.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|