mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-16 18:56:02 +00:00
Compare commits
1 Commits
codex/plug
...
codex/surv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5d71597f1 |
@@ -197,11 +197,10 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
|
|||||||
self,
|
self,
|
||||||
file_bytes: bytes,
|
file_bytes: bytes,
|
||||||
task_context: taskmgr.TaskContext | None,
|
task_context: taskmgr.TaskContext | None,
|
||||||
) -> tuple[str | None, str | None, str | None]:
|
) -> tuple[str | None, str | None]:
|
||||||
"""Extract plugin identity and dependency metadata from a plugin package."""
|
"""Extract plugin identity and dependency metadata from a plugin package."""
|
||||||
plugin_author = None
|
plugin_author = None
|
||||||
plugin_name = None
|
plugin_name = None
|
||||||
plugin_version = None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with zipfile.ZipFile(io.BytesIO(file_bytes)) as zf:
|
with zipfile.ZipFile(io.BytesIO(file_bytes)) as zf:
|
||||||
@@ -210,7 +209,6 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
|
|||||||
metadata = manifest.get('metadata', {})
|
metadata = manifest.get('metadata', {})
|
||||||
plugin_author = metadata.get('author')
|
plugin_author = metadata.get('author')
|
||||||
plugin_name = metadata.get('name')
|
plugin_name = metadata.get('name')
|
||||||
plugin_version = metadata.get('version')
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -229,7 +227,7 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return plugin_author, plugin_name, plugin_version
|
return plugin_author, plugin_name
|
||||||
|
|
||||||
async def _install_mcp_from_marketplace(
|
async def _install_mcp_from_marketplace(
|
||||||
self,
|
self,
|
||||||
@@ -371,7 +369,6 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
|
|||||||
):
|
):
|
||||||
plugin_author = install_info.get('plugin_author')
|
plugin_author = install_info.get('plugin_author')
|
||||||
plugin_name = install_info.get('plugin_name')
|
plugin_name = install_info.get('plugin_name')
|
||||||
plugin_file_transferred = False
|
|
||||||
|
|
||||||
if install_source == PluginInstallSource.MARKETPLACE:
|
if install_source == PluginInstallSource.MARKETPLACE:
|
||||||
# Handle marketplace plugin/mcp/skill installation
|
# Handle marketplace plugin/mcp/skill installation
|
||||||
@@ -466,18 +463,9 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
|
|||||||
)
|
)
|
||||||
|
|
||||||
file_bytes = download_resp.content
|
file_bytes = download_resp.content
|
||||||
plugin_author, plugin_name, plugin_version = self._inspect_plugin_package(
|
self._inspect_plugin_package(file_bytes, task_context)
|
||||||
file_bytes,
|
|
||||||
task_context,
|
|
||||||
)
|
|
||||||
if task_context is not None and plugin_author and plugin_name:
|
|
||||||
task_context.metadata['plugin_name'] = f'{plugin_author}/{plugin_name}'
|
|
||||||
if task_context is not None and plugin_version:
|
|
||||||
task_context.metadata['plugin_version'] = plugin_version
|
|
||||||
file_key = await self.handler.send_file(file_bytes, 'lbpkg')
|
file_key = await self.handler.send_file(file_bytes, 'lbpkg')
|
||||||
install_info['plugin_file_key'] = file_key
|
install_info['plugin_file_key'] = file_key
|
||||||
install_source = PluginInstallSource.LOCAL
|
|
||||||
plugin_file_transferred = True
|
|
||||||
self.ap.logger.info(f'Transfered file {file_key} to plugin runtime')
|
self.ap.logger.info(f'Transfered file {file_key} to plugin runtime')
|
||||||
# Continue to install via runtime
|
# Continue to install via runtime
|
||||||
else:
|
else:
|
||||||
@@ -493,14 +481,12 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
|
|||||||
mcp_resp.raise_for_status()
|
mcp_resp.raise_for_status()
|
||||||
raise Exception(f'Failed to get MCP {plugin_author}/{plugin_name}')
|
raise Exception(f'Failed to get MCP {plugin_author}/{plugin_name}')
|
||||||
|
|
||||||
if install_source == PluginInstallSource.LOCAL and not plugin_file_transferred:
|
if install_source == PluginInstallSource.LOCAL:
|
||||||
# transfer file before install
|
# transfer file before install
|
||||||
file_bytes = install_info['plugin_file']
|
file_bytes = install_info['plugin_file']
|
||||||
plugin_author, plugin_name, plugin_version = self._inspect_plugin_package(file_bytes, task_context)
|
plugin_author, plugin_name = self._inspect_plugin_package(file_bytes, task_context)
|
||||||
if task_context is not None and plugin_author and plugin_name:
|
if task_context is not None and plugin_author and plugin_name:
|
||||||
task_context.metadata['plugin_name'] = f'{plugin_author}/{plugin_name}'
|
task_context.metadata['plugin_name'] = f'{plugin_author}/{plugin_name}'
|
||||||
if task_context is not None and plugin_version:
|
|
||||||
task_context.metadata['plugin_version'] = plugin_version
|
|
||||||
file_key = await self.handler.send_file(file_bytes, 'lbpkg')
|
file_key = await self.handler.send_file(file_bytes, 'lbpkg')
|
||||||
install_info['plugin_file_key'] = file_key
|
install_info['plugin_file_key'] = file_key
|
||||||
del install_info['plugin_file']
|
del install_info['plugin_file']
|
||||||
@@ -537,11 +523,9 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
|
|||||||
task_context.metadata['download_speed'] = downloaded / elapsed if elapsed > 0 else 0
|
task_context.metadata['download_speed'] = downloaded / elapsed if elapsed > 0 else 0
|
||||||
|
|
||||||
file_bytes = b''.join(chunks)
|
file_bytes = b''.join(chunks)
|
||||||
plugin_author, plugin_name, plugin_version = self._inspect_plugin_package(file_bytes, task_context)
|
plugin_author, plugin_name = self._inspect_plugin_package(file_bytes, task_context)
|
||||||
if task_context is not None and plugin_author and plugin_name:
|
if task_context is not None and plugin_author and plugin_name:
|
||||||
task_context.metadata['plugin_name'] = f'{plugin_author}/{plugin_name}'
|
task_context.metadata['plugin_name'] = f'{plugin_author}/{plugin_name}'
|
||||||
if task_context is not None and plugin_version:
|
|
||||||
task_context.metadata['plugin_version'] = plugin_version
|
|
||||||
file_key = await self.handler.send_file(file_bytes, 'lbpkg')
|
file_key = await self.handler.send_file(file_bytes, 'lbpkg')
|
||||||
install_info['plugin_file_key'] = file_key
|
install_info['plugin_file_key'] = file_key
|
||||||
self.ap.logger.info(f'Transfered file {file_key} to plugin runtime')
|
self.ap.logger.info(f'Transfered file {file_key} to plugin runtime')
|
||||||
|
|||||||
@@ -49,30 +49,6 @@ class TestExtractDepsMetadata:
|
|||||||
assert 'flask' in task_context.metadata['deps_list']
|
assert 'flask' in task_context.metadata['deps_list']
|
||||||
assert 'numpy' in task_context.metadata['deps_list']
|
assert 'numpy' in task_context.metadata['deps_list']
|
||||||
|
|
||||||
def test_extract_plugin_identity_includes_version(self):
|
|
||||||
"""Extract plugin identity and version from manifest.yaml."""
|
|
||||||
connector = self._create_connector()
|
|
||||||
|
|
||||||
zip_buffer = io.BytesIO()
|
|
||||||
with zipfile.ZipFile(zip_buffer, 'w') as zf:
|
|
||||||
zf.writestr(
|
|
||||||
'manifest.yaml',
|
|
||||||
'\n'.join(
|
|
||||||
[
|
|
||||||
'metadata:',
|
|
||||||
' author: langbot-team',
|
|
||||||
' name: LangRAG',
|
|
||||||
' version: 0.1.8',
|
|
||||||
]
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert connector._inspect_plugin_package(zip_buffer.getvalue(), None) == (
|
|
||||||
'langbot-team',
|
|
||||||
'LangRAG',
|
|
||||||
'0.1.8',
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_extract_deps_empty_requirements(self):
|
def test_extract_deps_empty_requirements(self):
|
||||||
"""Handle empty requirements.txt."""
|
"""Handle empty requirements.txt."""
|
||||||
connector = self._create_connector()
|
connector = self._create_connector()
|
||||||
|
|||||||
@@ -310,6 +310,7 @@ function SingleSelectField({
|
|||||||
{options.map((opt) => (
|
{options.map((opt) => (
|
||||||
<div key={opt.id}>
|
<div key={opt.id}>
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={() => onChange(opt.id)}
|
onClick={() => onChange(opt.id)}
|
||||||
className={`w-full text-left text-sm px-3 py-2 rounded-lg border transition-colors ${
|
className={`w-full text-left text-sm px-3 py-2 rounded-lg border transition-colors ${
|
||||||
value === opt.id
|
value === opt.id
|
||||||
@@ -361,8 +362,16 @@ function MultiSelectField({
|
|||||||
const selected = value.includes(opt.id);
|
const selected = value.includes(opt.id);
|
||||||
return (
|
return (
|
||||||
<div key={opt.id}>
|
<div key={opt.id}>
|
||||||
<button
|
<div
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
onClick={() => toggle(opt.id)}
|
onClick={() => toggle(opt.id)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
toggle(opt.id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
className={`w-full text-left text-sm px-3 py-2 rounded-lg border transition-colors flex items-center gap-2 ${
|
className={`w-full text-left text-sm px-3 py-2 rounded-lg border transition-colors flex items-center gap-2 ${
|
||||||
selected
|
selected
|
||||||
? 'border-primary bg-primary/5 text-primary'
|
? 'border-primary bg-primary/5 text-primary'
|
||||||
@@ -371,7 +380,7 @@ function MultiSelectField({
|
|||||||
>
|
>
|
||||||
<Checkbox checked={selected} className="pointer-events-none" />
|
<Checkbox checked={selected} className="pointer-events-none" />
|
||||||
{getI18nText(opt.label)}
|
{getI18nText(opt.label)}
|
||||||
</button>
|
</div>
|
||||||
{opt.has_input && selected && (
|
{opt.has_input && selected && (
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
|||||||
Reference in New Issue
Block a user