Skip to content

Commit 8a83bbf

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

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]]).';

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,6 @@
7474

7575
$_lang['setting_modai.cache.lit'] = 'Last Install Time';
7676
$_lang['setting_modai.cache.lit_desc'] = '';
77+
78+
$_lang['setting_modai.contexts.resources.name'] = 'Context Provider Name';
79+
$_lang['setting_modai.contexts.resources.name_desc'] = 'Name of the context provider that will be used to index resources. It has to be enabled.';

core/components/modai/schema/modai.mysql.schema.xml

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -74,24 +74,39 @@
7474
<index alias="name" name="name" primary="false" unique="true" type="BTREE">
7575
<column key="name" length="" collation="A" null="false" />
7676
</index>
77+
7778
<index alias="enabled" name="enabled" primary="false" unique="false" type="BTREE">
7879
<column key="enabled" length="" collation="A" null="false" />
7980
</index>
81+
8082
<index alias="class" name="class" primary="false" unique="false" type="BTREE">
8183
<column key="class" length="" collation="A" null="false" />
8284
</index>
8385

8486
<composite alias="AgentTools" cardinality="many" class="modAI\Model\AgentTool" foreign="tool_id" local="id" owner="local" />
8587
</object>
8688

87-
<!-- <object class="ContextProvider" table="modai_context_provider">-->
88-
<!-- <field key="class" dbtype="varchar" precision="200" phptype="string" null="false" default="" />-->
89-
<!-- <field key="name" dbtype="varchar" precision="200" phptype="string" null="false" default="" />-->
90-
<!-- <field key="description" dbtype="varchar" precision="500" phptype="string" null="true" default="" />-->
91-
<!-- <field key="properties" dbtype="text" phptype="array" null="true" default="" />-->
89+
<object class="ContextProvider" table="modai_context_providers">
90+
<field key="class" dbtype="varchar" precision="200" phptype="string" null="false" />
91+
<field key="name" dbtype="varchar" precision="200" phptype="string" null="false" />
92+
<field key="description" dbtype="varchar" precision="500" phptype="string" null="true" default="" />
93+
<field key="config" dbtype="text" phptype="json" null="false" default="{}" />
94+
<field key="enabled" dbtype="tinyint" precision="1" phptype="boolean" null="false" default="0" />
9295

93-
<!-- <composite alias="Agents" cardinality="many" class="modAI\Model\AgentContextProvider" foreign="context_provider_id" local="id" owner="local" />-->
94-
<!-- </object>-->
96+
<index alias="name" name="name" primary="false" unique="true" type="BTREE">
97+
<column key="name" length="" collation="A" null="false" />
98+
</index>
99+
100+
<index alias="enabled" name="enabled" primary="false" unique="false" type="BTREE">
101+
<column key="enabled" length="" collation="A" null="false" />
102+
</index>
103+
104+
<index alias="class" name="class" primary="false" unique="false" type="BTREE">
105+
<column key="class" length="" collation="A" null="false" />
106+
</index>
107+
108+
<composite alias="AgentContextProviders" cardinality="many" class="modAI\Model\AgentContextProvider" foreign="context_provider_id" local="id" owner="local" />
109+
</object>
95110

96111
<object class="Agent" table="modai_agents">
97112
<field key="name" dbtype="varchar" precision="200" phptype="string" null="false" />
@@ -108,25 +123,29 @@
108123
</index>
109124

110125
<composite alias="AgentTools" cardinality="many" class="modAI\Model\AgentTool" foreign="agent_id" local="id" owner="local" />
111-
<!-- <composite alias="ContextProviders" cardinality="many" class="modAI\Model\AgentContextProvider" foreign="agent_id" local="id" owner="local" />-->
126+
<composite alias="AgentContextProviders" cardinality="many" class="modAI\Model\AgentContextProvider" foreign="agent_id" local="id" owner="local" />
112127
</object>
113128

114-
<!-- <object class="AgentContextProvider" table="modai_agent_context_provider">-->
115-
<!-- <field key="agent_id" dbtype="int" precision="10" phptype="int" null="false" default="0"-->
116-
<!-- attributes="unsigned"/>-->
117-
<!-- <field key="context_provider_id" dbtype="int" precision="10" phptype="int" null="false" default="0"-->
118-
<!-- attributes="unsigned"/>-->
129+
<object class="AgentContextProvider" table="modai_agent_context_providers" extends="xPDO\Om\xPDOObject">
130+
<field key="agent_id" dbtype="int" attributes="unsigned" precision="10" phptype="int" null="false" index="pk" />
131+
<field key="context_provider_id" dbtype="int" attributes="unsigned" precision="10" phptype="int" null="false" index="pk" />
119132

120-
<!-- <index alias="agent_id" name="agent_id" primary="false" unique="false" type="BTREE">-->
121-
<!-- <column key="agent_id" length="" collation="A" null="false"/>-->
122-
<!-- </index>-->
123-
<!-- <index alias="context_provider_id" name="context_provider_id" primary="false" unique="false" type="BTREE">-->
124-
<!-- <column key="context_provider_id" length="" collation="A" null="false"/>-->
125-
<!-- </index>-->
133+
<index alias="PRIMARY" name="PRIMARY" primary="true" unique="true" type="BTREE">
134+
<column key="agent_id" length="" collation="A" null="false" />
135+
<column key="context_provider_id" length="" collation="A" null="false" />
136+
</index>
126137

127-
<!-- <aggregate alias="Agent" cardinality="one" class="modAI\Model\Agent" foreign="id" local="agent_id" owner="foreign" />-->
128-
<!-- <aggregate alias="ContextProvider" cardinality="one" class="modAI\Model\ContextProvider" foreign="id" local="context_provider_id" owner="foreign" />-->
129-
<!-- </object>-->
138+
<index alias="agent_id" name="agent_id" primary="false" unique="false" type="BTREE">
139+
<column key="agent_id" length="" collation="A" null="false"/>
140+
</index>
141+
142+
<index alias="context_provider_id" name="context_provider_id" primary="false" unique="false" type="BTREE">
143+
<column key="context_provider_id" length="" collation="A" null="false"/>
144+
</index>
145+
146+
<aggregate alias="Agent" cardinality="one" class="modAI\Model\Agent" foreign="id" local="agent_id" owner="foreign" />
147+
<aggregate alias="ContextProvider" cardinality="one" class="modAI\Model\ContextProvider" foreign="id" local="context_provider_id" owner="foreign" />
148+
</object>
130149

131150
<object class="AgentTool" table="modai_agent_tools" extends="xPDO\Om\xPDOObject">
132151
<field key="agent_id" dbtype="int" attributes="unsigned" precision="10" phptype="int" null="false" index="pk" />

0 commit comments

Comments
 (0)