Skip to content

Commit 8a83bbf

Browse files
committed
feat: add support for context providers
1 parent 70e87b5 commit 8a83bbf

File tree

30 files changed

+922
-46
lines changed

30 files changed

+922
-46
lines changed

_build/gpm.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ plugins:
88
file: modai.php
99
events:
1010
- OnManagerPageBeforeRender
11+
- name: modAIContext
12+
file: modaiContext.php
13+
events:
14+
- OnDocFormSave
15+
- OnDocFormDelete
16+
- OnResourceUndelete
1117

1218
systemSettings:
1319
- key: cache.lit
@@ -159,6 +165,10 @@ systemSettings:
159165
area: tvs
160166
value: ''
161167

168+
- key: contexts.resources.name
169+
area: contexts
170+
value: ''
171+
162172
build:
163173
scriptsAfter:
164174
- lit.gpm.php
@@ -169,3 +179,5 @@ database:
169179
- "modAI\\Model\\Tool"
170180
- "modAI\\Model\\Agent"
171181
- "modAI\\Model\\AgentTool"
182+
- "modAI\\Model\\ContextProvider"
183+
- "modAI\\Model\\AgentContextProvider"

_build/js/src/executor/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ export const executor = {
3030
);
3131
},
3232
},
33+
context: {
34+
get: async (params: { prompt: string; agent: string }, controller?: AbortController) => {
35+
return await modxFetch<{ contexts: string[] }>('Context\\Get', params, controller);
36+
},
37+
},
3338
prompt: {
3439
/**
3540
* @deprecated use 'chat' instead

_build/js/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export type Config = {
1111
apiURL: string;
1212
cssURL: string;
1313
translateFn?: (key: string, params?: Record<string, string>) => string;
14-
availableAgents: string[];
14+
availableAgents: Record<string, { name: string; contextProviders: string[] | null }>;
1515
};
1616

1717
export const init = (config: Config) => {

_build/js/src/ui/localChat/modalActions.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,27 @@ export const sendMessage = async (
104104
renderer: at.renderer,
105105
value: at.value,
106106
}))
107-
: undefined;
107+
: [];
108108

109109
globalState.modal.attachments.removeAttachments();
110110
globalState.modal.context.removeContexts();
111111

112112
const messages = globalState.modal.history.getMessagesHistory();
113113
globalState.modal.history.addUserMessage({ content: message, attachments, contexts }, hidePrompt);
114114

115+
const agent = globalState.config.availableAgents[globalState.modal.agent.value];
116+
if (agent && agent.contextProviders && agent.contextProviders.length > 0) {
117+
const remoteContexts = await executor.mgr.context.get({ prompt: message, agent: agent.name });
118+
remoteContexts.contexts.map((ctx) => {
119+
contexts.push({
120+
__type: 'ContextProvider',
121+
name: 'ContextProvider',
122+
renderer: undefined,
123+
value: ctx,
124+
});
125+
});
126+
}
127+
115128
try {
116129
if (config.type === 'text') {
117130
const data = await executor.mgr.prompt.chat(

_build/js/src/ui/localChat/modalInput.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ export const buildModalInput = (config: LocalChatConfig) => {
176176
const agent = createElement('select', undefined, [
177177
createElement('option', undefined, 'No Agent', { value: '' }),
178178
...Object.values(globalState.config.availableAgents).map((agent) =>
179-
createElement('option', undefined, agent, { value: agent }),
179+
createElement('option', undefined, agent.name, { value: agent.name }),
180180
),
181181
]);
182182

_build/scripts/seed.gpm.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,32 @@ public function __invoke(&$modx, $action)
4545
$toolObjects->save();
4646
}
4747

48+
$contextProviders = [
49+
[
50+
'name' => 'resources',
51+
'description' => 'Pinecone instance for storing resources',
52+
'class' => \modAI\ContextProviders\Pinecone::class,
53+
'config' => [
54+
'endpoint' => 'ss:pinecone_url',
55+
'api_key' => 'ss:pinecone_key',
56+
'namespace' => 'resources',
57+
'fields' => 'pagetitle,introtext,content',
58+
'intro_msg' => 'Potential relevant context from resource {id}:',
59+
],
60+
'enabled' => true,
61+
]
62+
];
63+
64+
foreach ($contextProviders as $contextProvider) {
65+
$exists = $this->modx->getCount(\modAI\Model\ContextProvider::class, ['name' => $contextProvider['name']]);
66+
if ($exists > 0) {
67+
continue;
68+
}
69+
70+
$contextProviderObjects = $this->modx->newObject(\modAI\Model\ContextProvider::class, $contextProvider);
71+
$contextProviderObjects->save();
72+
}
73+
4874
$agents = [
4975
[
5076
'name' => 'RedneckWeatherMan',
@@ -54,6 +80,18 @@ public function __invoke(&$modx, $action)
5480
'tools' => [
5581
\modAI\Tools\GetWeather::getSuggestedName(),
5682
]
83+
],
84+
[
85+
'name' => 'ContentWriter',
86+
'description' => 'Writes a decent content',
87+
'prompt' => '',
88+
'enabled' => true,
89+
'tools' => [
90+
\modAI\Tools\GetWeather::getSuggestedName(),
91+
],
92+
'contextProviders' => [
93+
'resources'
94+
]
5795
]
5896
];
5997

@@ -86,6 +124,20 @@ public function __invoke(&$modx, $action)
86124
$agentTool->save();
87125
}
88126
}
127+
128+
if (!empty($agent['contextProviders'])) {
129+
foreach ($agent['contextProviders'] as $contextProviderName) {
130+
$contextProvider = $this->modx->getObject(\modAI\Model\ContextProvider::class, ['name' => $contextProviderName]);
131+
if (!$contextProvider) {
132+
continue;
133+
}
134+
135+
$agentContextProvider = $this->modx->newObject(\modAI\Model\AgentContextProvider::class);
136+
$agentContextProvider->set('agent_id', $agentObject->id);
137+
$agentContextProvider->set('context_provider_id', $contextProvider->id);
138+
$agentContextProvider->save();
139+
}
140+
}
89141
}
90142

91143
return true;

assets/components/modai/js/modai.js

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/components/modai/elements/plugins/modai.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,4 @@
4949
');
5050

5151
$modx->regClientStartupScript($modAI->getJSFile());
52-
}
52+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
/**
3+
* @var \MODX\Revolution\modX $modx
4+
*/
5+
6+
if (!$modx->services->has('modai')) {
7+
return;
8+
}
9+
10+
/** @var \modAI\modAI | null $modAI */
11+
$modAI = $modx->services->get('modai');
12+
13+
if ($modAI === null) {
14+
return;
15+
}
16+
17+
if ($modx->event->name === 'OnDocFormSave' || $modx->event->name === 'OnResourceUndelete') {
18+
/**
19+
* @var \MODX\Revolution\modResource $resource
20+
*/
21+
22+
if ($resource->get('deleted')) {
23+
return;
24+
}
25+
26+
$contextName = $modx->getOption('modai.contexts.resources.name');
27+
if (empty($contextName)) {
28+
return;
29+
}
30+
31+
/** @var \modAI\Model\ContextProvider $provider */
32+
$provider = $modx->getObject(\modAI\Model\ContextProvider::class, ['enabled' => true, 'name' => $contextName, 'class' => \modAI\ContextProviders\Pinecone::class]);
33+
if (!$provider) {
34+
return;
35+
}
36+
37+
try {
38+
/** @var \modAI\ContextProviders\Pinecone $instance */
39+
$instance = $provider->getContextProviderInstance();
40+
41+
42+
$data = $resource->toArray();
43+
foreach ($data as $key => $value) {
44+
if (is_array($value)) {
45+
$value = json_encode($value);
46+
}
47+
48+
$data[$key] = strip_tags($value);
49+
}
50+
51+
$instance->index('resource', $resource->get('id'), $data);
52+
} catch (\Throwable $e) {
53+
$modx->log(modX::LOG_LEVEL_ERROR, '[modai] context plugin: ' . $e->getMessage());
54+
return;
55+
}
56+
57+
58+
return;
59+
}
60+
61+
if ($modx->event->name === 'OnDocFormDelete') {
62+
$contextName = $modx->getOption('modai.contexts.resources.name');
63+
if (empty($contextName)) {
64+
return;
65+
}
66+
67+
/** @var \modAI\Model\ContextProvider $provider */
68+
$provider = $modx->getObject(\modAI\Model\ContextProvider::class, ['enabled' => true, 'name' => $contextName, 'class' => \modAI\ContextProviders\Pinecone::class]);
69+
if (!$provider) {
70+
return;
71+
}
72+
73+
try {
74+
/** @var \modAI\ContextProviders\Pinecone $instance */
75+
$instance = $provider->getContextProviderInstance();
76+
/**
77+
* @var $id
78+
* @var $children
79+
*/
80+
$instance->delete(array_merge([$id], is_array($children) ? $children : []));
81+
82+
} catch (\Throwable $e) {
83+
$modx->log(modX::LOG_LEVEL_ERROR, '[modai] context plugin: ' . $e->getMessage());
84+
return;
85+
}
86+
87+
88+
return;
89+
}

core/components/modai/lexicon/en/default.inc.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,8 @@
6767
$_lang['modai.error.tool_not_available'] = 'Tool class not available: [[+class]]';
6868
$_lang['modai.error.tool_wrong_interface'] = 'Tool does not implement the \modAI\Tools\ToolInterface';
6969
$_lang['modai.error.tool_instance_err'] = 'Tool could not be instantiated: [[+msg]]';
70+
$_lang['modai.error.context_provider_not_available'] = 'Context Provider class not available: [[+class]]';
71+
$_lang['modai.error.context_provider_wrong_interface'] = 'Context Provider does not implement the \modAI\ContextProviders\ContextProviderInterface';
72+
$_lang['modai.error.context_provider_instance_err'] = 'Context Provider could not be instantiated: [[+msg]]';
7073
$_lang['modai.error.invalid_agent'] = 'Invalid agent.';
74+
$_lang['modai.error.invalid_context_provider_config'] = 'Invalid configuration for context provider [[+name]] ([[+class]]).';

0 commit comments

Comments
 (0)