dulus 0.2.60__tar.gz → 3.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.
- {dulus-0.2.60 → dulus-3.4.0}/MANIFEST.in +3 -0
- dulus-3.4.0/PKG-INFO +167 -0
- dulus-3.4.0/README.md +102 -0
- {dulus-0.2.60 → dulus-3.4.0}/agent.py +61 -0
- {dulus-0.2.60 → dulus-3.4.0}/backend/agents_bridge.py +8 -3
- {dulus-0.2.60 → dulus-3.4.0}/backend/compressor.py +1 -1
- {dulus-0.2.60 → dulus-3.4.0}/batch_api.py +165 -0
- {dulus-0.2.60 → dulus-3.4.0}/compaction.py +280 -40
- {dulus-0.2.60 → dulus-3.4.0}/config.py +12 -3
- {dulus-0.2.60 → dulus-3.4.0}/context.py +101 -2
- {dulus-0.2.60 → dulus-3.4.0}/data/plugins/composio/composio_plugin/tool_generator.py +2 -2
- {dulus-0.2.60 → dulus-3.4.0}/data/plugins/composio/plugin_tool.py +15 -15
- dulus-3.4.0/docs/API.md +343 -0
- dulus-3.4.0/docs/BRAND_GUIDE.md +289 -0
- dulus-3.4.0/docs/CHANGELOG.md +117 -0
- dulus-3.4.0/docs/CONTRIBUTING.md +375 -0
- dulus-3.4.0/docs/DEPLOYMENT.md +293 -0
- dulus-3.4.0/docs/Dulus_AI_Whitepaper_(v2.0).pdf +0 -0
- dulus-3.4.0/docs/FAQ.md +230 -0
- dulus-3.4.0/docs/GETTING_STARTED.md +397 -0
- dulus-3.4.0/docs/INVESTOR_BRIEF.md +324 -0
- dulus-0.2.60/dulus.egg-info/PKG-INFO → dulus-3.4.0/docs/README.md +223 -96
- dulus-3.4.0/docs/README_AR.md +240 -0
- dulus-3.4.0/docs/README_EN.md +318 -0
- dulus-3.4.0/docs/README_ES.md +251 -0
- dulus-3.4.0/docs/README_FR.md +240 -0
- dulus-3.4.0/docs/README_JA.md +240 -0
- dulus-3.4.0/docs/README_KO.md +240 -0
- dulus-3.4.0/docs/README_PT.md +240 -0
- dulus-3.4.0/docs/README_RU.md +240 -0
- dulus-3.4.0/docs/README_ZH.md +240 -0
- dulus-3.4.0/docs/SECURITY.md +162 -0
- dulus-3.4.0/docs/api.html +2499 -0
- dulus-3.4.0/docs/architecture.md +428 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/dashboard/index.html +5 -8
- dulus-3.4.0/docs/dulus-bird.png +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/hero.svg +4 -3
- {dulus-0.2.60 → dulus-3.4.0}/docs/index.html +1686 -325
- {dulus-0.2.60 → dulus-3.4.0}/docs/news.md +40 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/preview.html +1 -1
- dulus-3.4.0/dulus.egg-info/PKG-INFO +167 -0
- dulus-3.4.0/dulus.egg-info/SOURCES.txt +258 -0
- {dulus-0.2.60 → dulus-3.4.0}/dulus.egg-info/requires.txt +18 -0
- {dulus-0.2.60 → dulus-3.4.0}/dulus.egg-info/top_level.txt +6 -1
- {dulus-0.2.60 → dulus-3.4.0}/dulus.py +12175 -10178
- dulus-3.4.0/dulus_gui.py +818 -0
- dulus-3.4.0/dulus_tools/__init__.py +1 -0
- dulus-3.4.0/dulus_tools/add_dir_manager.py +109 -0
- dulus-3.4.0/dulus_tools/afk_mode.py +34 -0
- dulus-3.4.0/dulus_tools/approval_runtime.py +285 -0
- dulus-3.4.0/dulus_tools/background_tasks.py +847 -0
- dulus-3.4.0/dulus_tools/clipboard_utils.py +166 -0
- dulus-3.4.0/dulus_tools/diff_visualizer.py +147 -0
- dulus-3.4.0/dulus_tools/export_import.py +206 -0
- dulus-3.4.0/dulus_tools/hook_engine.py +379 -0
- dulus-3.4.0/dulus_tools/notification_manager.py +344 -0
- dulus-3.4.0/dulus_tools/session_fork.py +230 -0
- dulus-3.4.0/dulus_tools/shell_mode.py +130 -0
- dulus-3.4.0/dulus_tools/todo_visualizer.py +131 -0
- dulus-3.4.0/dulus_tools/wire_events.py +208 -0
- dulus-3.4.0/dulus_tools/yolo_mode.py +35 -0
- dulus-3.4.0/governance.py +277 -0
- dulus-3.4.0/gui/__init__.py +45 -0
- dulus-3.4.0/gui/main_window.py +1529 -0
- dulus-3.4.0/gui/sandbox_server.py +152 -0
- {dulus-0.2.60 → dulus-3.4.0}/gui/themes.py +31 -0
- {dulus-0.2.60 → dulus-3.4.0}/input.py +66 -9
- {dulus-0.2.60 → dulus-3.4.0}/license_manager.py +1 -1
- {dulus-0.2.60 → dulus-3.4.0}/offload_helper.py +58 -13
- dulus-3.4.0/personality_config.py +353 -0
- {dulus-0.2.60 → dulus-3.4.0}/plugin/autoadapter.py +84 -5
- {dulus-0.2.60 → dulus-3.4.0}/providers.py +5453 -3896
- {dulus-0.2.60 → dulus-3.4.0}/pyproject.toml +45 -7
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/dist/index.html +1 -0
- dulus-3.4.0/sandbox_bootstrap.py +54 -0
- dulus-3.4.0/skill/bundled/LICENSE-obsidian-skills +21 -0
- dulus-3.4.0/skill/bundled/README.md +40 -0
- dulus-3.4.0/skill/bundled/defuddle/SKILL.md +41 -0
- dulus-3.4.0/skill/bundled/json-canvas/SKILL.md +244 -0
- dulus-3.4.0/skill/bundled/json-canvas/references/EXAMPLES.md +329 -0
- dulus-3.4.0/skill/bundled/obsidian-bases/SKILL.md +497 -0
- dulus-3.4.0/skill/bundled/obsidian-bases/references/FUNCTIONS_REFERENCE.md +173 -0
- dulus-3.4.0/skill/bundled/obsidian-cli/SKILL.md +106 -0
- dulus-3.4.0/skill/bundled/obsidian-markdown/SKILL.md +198 -0
- dulus-3.4.0/skill/bundled/obsidian-markdown/references/CALLOUTS.md +58 -0
- dulus-3.4.0/skill/bundled/obsidian-markdown/references/EMBEDS.md +63 -0
- dulus-3.4.0/skill/bundled/obsidian-markdown/references/PROPERTIES.md +61 -0
- {dulus-0.2.60 → dulus-3.4.0}/skill/clawhub.py +198 -0
- {dulus-0.2.60 → dulus-3.4.0}/skill/loader.py +36 -6
- dulus-3.4.0/soul.py +450 -0
- {dulus-0.2.60 → dulus-3.4.0}/tmux_offloader.py +23 -6
- {dulus-0.2.60 → dulus-3.4.0}/tools.py +212 -21
- {dulus-0.2.60 → dulus-3.4.0}/voice/audio_utils.py +25 -9
- {dulus-0.2.60 → dulus-3.4.0}/voice/recorder.py +8 -1
- {dulus-0.2.60 → dulus-3.4.0}/voice/stt.py +56 -25
- {dulus-0.2.60 → dulus-3.4.0}/voice/tts.py +93 -1
- {dulus-0.2.60 → dulus-3.4.0}/voice/wake_word.py +171 -136
- {dulus-0.2.60 → dulus-3.4.0}/webbridge/__init__.py +5 -5
- {dulus-0.2.60 → dulus-3.4.0}/webbridge/core.py +694 -538
- dulus-3.4.0/webbridge/tools.py +453 -0
- {dulus-0.2.60 → dulus-3.4.0}/webchat_server.py +117 -23
- dulus-3.4.0/welcome.py +675 -0
- dulus-0.2.60/PKG-INFO +0 -712
- dulus-0.2.60/README.md +0 -662
- dulus-0.2.60/data/context.json +0 -143
- dulus-0.2.60/docs/README.md +0 -510
- dulus-0.2.60/docs/api.html +0 -2894
- dulus-0.2.60/docs/architecture.md +0 -374
- dulus-0.2.60/docs/particle-playground.html +0 -328
- dulus-0.2.60/dulus.egg-info/SOURCES.txt +0 -365
- dulus-0.2.60/dulus_gui.py +0 -387
- dulus-0.2.60/gui/__init__.py +0 -18
- dulus-0.2.60/gui/main_window.py +0 -485
- dulus-0.2.60/sandbox/public/wallpaper-default.jpg +0 -0
- dulus-0.2.60/sandbox/public/wallpapers/default.jpeg +0 -0
- dulus-0.2.60/sandbox/public/wallpapers/light.jpeg +0 -0
- dulus-0.2.60/sandbox/public/wallpapers/nature.jpeg +0 -0
- dulus-0.2.60/sandbox/public/wallpapers/tech.jpeg +0 -0
- dulus-0.2.60/sandbox/src/App.tsx +0 -241
- dulus-0.2.60/sandbox/src/apps/AgentMonitor.tsx +0 -249
- dulus-0.2.60/sandbox/src/apps/ApiTester.tsx +0 -479
- dulus-0.2.60/sandbox/src/apps/AppRouter.tsx +0 -197
- dulus-0.2.60/sandbox/src/apps/ArchiveManager.tsx +0 -360
- dulus-0.2.60/sandbox/src/apps/AsciiArt.tsx +0 -393
- dulus-0.2.60/sandbox/src/apps/Base64Tool.tsx +0 -364
- dulus-0.2.60/sandbox/src/apps/Browser.tsx +0 -568
- dulus-0.2.60/sandbox/src/apps/Calculator.tsx +0 -358
- dulus-0.2.60/sandbox/src/apps/Calendar.tsx +0 -562
- dulus-0.2.60/sandbox/src/apps/Chat.tsx +0 -319
- dulus-0.2.60/sandbox/src/apps/Chess.tsx +0 -570
- dulus-0.2.60/sandbox/src/apps/Clock.tsx +0 -565
- dulus-0.2.60/sandbox/src/apps/CodeEditor.tsx +0 -467
- dulus-0.2.60/sandbox/src/apps/ColorPalette.tsx +0 -386
- dulus-0.2.60/sandbox/src/apps/ColorPicker.tsx +0 -359
- dulus-0.2.60/sandbox/src/apps/Contacts.tsx +0 -238
- dulus-0.2.60/sandbox/src/apps/DocumentViewer.tsx +0 -275
- dulus-0.2.60/sandbox/src/apps/Drawing.tsx +0 -413
- dulus-0.2.60/sandbox/src/apps/Email.tsx +0 -464
- dulus-0.2.60/sandbox/src/apps/FileManager.tsx +0 -413
- dulus-0.2.60/sandbox/src/apps/FlappyBird.tsx +0 -297
- dulus-0.2.60/sandbox/src/apps/FtpClient.tsx +0 -255
- dulus-0.2.60/sandbox/src/apps/Game2048.tsx +0 -280
- dulus-0.2.60/sandbox/src/apps/GitClient.tsx +0 -378
- dulus-0.2.60/sandbox/src/apps/ImageGallery.tsx +0 -276
- dulus-0.2.60/sandbox/src/apps/ImageViewer.tsx +0 -272
- dulus-0.2.60/sandbox/src/apps/JsonFormatter.tsx +0 -402
- dulus-0.2.60/sandbox/src/apps/MarkdownPreview.tsx +0 -293
- dulus-0.2.60/sandbox/src/apps/MatrixRain.tsx +0 -253
- dulus-0.2.60/sandbox/src/apps/MediaConverter.tsx +0 -253
- dulus-0.2.60/sandbox/src/apps/Memory.tsx +0 -269
- dulus-0.2.60/sandbox/src/apps/MemoryManager.tsx +0 -263
- dulus-0.2.60/sandbox/src/apps/Minesweeper.tsx +0 -346
- dulus-0.2.60/sandbox/src/apps/MusicPlayer.tsx +0 -306
- dulus-0.2.60/sandbox/src/apps/NetworkTools.tsx +0 -321
- dulus-0.2.60/sandbox/src/apps/Notes.tsx +0 -420
- dulus-0.2.60/sandbox/src/apps/PasswordManager.tsx +0 -212
- dulus-0.2.60/sandbox/src/apps/PhotoEditor.tsx +0 -342
- dulus-0.2.60/sandbox/src/apps/Pong.tsx +0 -349
- dulus-0.2.60/sandbox/src/apps/RegexTester.tsx +0 -432
- dulus-0.2.60/sandbox/src/apps/Reminders.tsx +0 -228
- dulus-0.2.60/sandbox/src/apps/RssReader.tsx +0 -245
- dulus-0.2.60/sandbox/src/apps/ScreenRecorder.tsx +0 -416
- dulus-0.2.60/sandbox/src/apps/Settings.tsx +0 -474
- dulus-0.2.60/sandbox/src/apps/SkillsLauncher.tsx +0 -316
- dulus-0.2.60/sandbox/src/apps/Snake.tsx +0 -274
- dulus-0.2.60/sandbox/src/apps/Solitaire.tsx +0 -445
- dulus-0.2.60/sandbox/src/apps/Spreadsheet.tsx +0 -441
- dulus-0.2.60/sandbox/src/apps/Sudoku.tsx +0 -386
- dulus-0.2.60/sandbox/src/apps/SystemMonitor.tsx +0 -360
- dulus-0.2.60/sandbox/src/apps/TaskManager.tsx +0 -392
- dulus-0.2.60/sandbox/src/apps/Terminal.tsx +0 -619
- dulus-0.2.60/sandbox/src/apps/Tetris.tsx +0 -441
- dulus-0.2.60/sandbox/src/apps/TextEditor.tsx +0 -446
- dulus-0.2.60/sandbox/src/apps/TicTacToe.tsx +0 -254
- dulus-0.2.60/sandbox/src/apps/Todo.tsx +0 -394
- dulus-0.2.60/sandbox/src/apps/VideoPlayer.tsx +0 -297
- dulus-0.2.60/sandbox/src/apps/VoiceRecorder.tsx +0 -382
- dulus-0.2.60/sandbox/src/apps/Weather.tsx +0 -462
- dulus-0.2.60/sandbox/src/apps/Whiteboard.tsx +0 -338
- dulus-0.2.60/sandbox/src/apps/registry.ts +0 -564
- dulus-0.2.60/sandbox/src/components/AppContainer.tsx +0 -26
- dulus-0.2.60/sandbox/src/components/AppLauncher.tsx +0 -241
- dulus-0.2.60/sandbox/src/components/BootSequence.tsx +0 -236
- dulus-0.2.60/sandbox/src/components/ContextMenu.tsx +0 -251
- dulus-0.2.60/sandbox/src/components/Desktop.tsx +0 -186
- dulus-0.2.60/sandbox/src/components/Dock.tsx +0 -199
- dulus-0.2.60/sandbox/src/components/LoginScreen.tsx +0 -302
- dulus-0.2.60/sandbox/src/components/NotImplemented.tsx +0 -55
- dulus-0.2.60/sandbox/src/components/NotificationCenter.tsx +0 -181
- dulus-0.2.60/sandbox/src/components/NotificationSystem.tsx +0 -136
- dulus-0.2.60/sandbox/src/components/TopPanel.tsx +0 -334
- dulus-0.2.60/sandbox/src/components/WindowFrame.tsx +0 -401
- dulus-0.2.60/sandbox/src/components/WindowManager.tsx +0 -25
- dulus-0.2.60/sandbox/src/components/ui/accordion.tsx +0 -64
- dulus-0.2.60/sandbox/src/components/ui/alert-dialog.tsx +0 -155
- dulus-0.2.60/sandbox/src/components/ui/alert.tsx +0 -66
- dulus-0.2.60/sandbox/src/components/ui/aspect-ratio.tsx +0 -11
- dulus-0.2.60/sandbox/src/components/ui/avatar.tsx +0 -51
- dulus-0.2.60/sandbox/src/components/ui/badge.tsx +0 -46
- dulus-0.2.60/sandbox/src/components/ui/breadcrumb.tsx +0 -109
- dulus-0.2.60/sandbox/src/components/ui/button-group.tsx +0 -83
- dulus-0.2.60/sandbox/src/components/ui/button.tsx +0 -62
- dulus-0.2.60/sandbox/src/components/ui/calendar.tsx +0 -220
- dulus-0.2.60/sandbox/src/components/ui/card.tsx +0 -92
- dulus-0.2.60/sandbox/src/components/ui/carousel.tsx +0 -239
- dulus-0.2.60/sandbox/src/components/ui/chart.tsx +0 -357
- dulus-0.2.60/sandbox/src/components/ui/checkbox.tsx +0 -32
- dulus-0.2.60/sandbox/src/components/ui/collapsible.tsx +0 -31
- dulus-0.2.60/sandbox/src/components/ui/command.tsx +0 -182
- dulus-0.2.60/sandbox/src/components/ui/context-menu.tsx +0 -252
- dulus-0.2.60/sandbox/src/components/ui/dialog.tsx +0 -141
- dulus-0.2.60/sandbox/src/components/ui/drawer.tsx +0 -135
- dulus-0.2.60/sandbox/src/components/ui/dropdown-menu.tsx +0 -255
- dulus-0.2.60/sandbox/src/components/ui/empty.tsx +0 -104
- dulus-0.2.60/sandbox/src/components/ui/field.tsx +0 -246
- dulus-0.2.60/sandbox/src/components/ui/form.tsx +0 -167
- dulus-0.2.60/sandbox/src/components/ui/hover-card.tsx +0 -44
- dulus-0.2.60/sandbox/src/components/ui/input-group.tsx +0 -170
- dulus-0.2.60/sandbox/src/components/ui/input-otp.tsx +0 -75
- dulus-0.2.60/sandbox/src/components/ui/input.tsx +0 -21
- dulus-0.2.60/sandbox/src/components/ui/item.tsx +0 -193
- dulus-0.2.60/sandbox/src/components/ui/kbd.tsx +0 -28
- dulus-0.2.60/sandbox/src/components/ui/label.tsx +0 -24
- dulus-0.2.60/sandbox/src/components/ui/menubar.tsx +0 -274
- dulus-0.2.60/sandbox/src/components/ui/navigation-menu.tsx +0 -168
- dulus-0.2.60/sandbox/src/components/ui/pagination.tsx +0 -127
- dulus-0.2.60/sandbox/src/components/ui/popover.tsx +0 -48
- dulus-0.2.60/sandbox/src/components/ui/progress.tsx +0 -29
- dulus-0.2.60/sandbox/src/components/ui/radio-group.tsx +0 -45
- dulus-0.2.60/sandbox/src/components/ui/resizable.tsx +0 -54
- dulus-0.2.60/sandbox/src/components/ui/scroll-area.tsx +0 -58
- dulus-0.2.60/sandbox/src/components/ui/select.tsx +0 -188
- dulus-0.2.60/sandbox/src/components/ui/separator.tsx +0 -28
- dulus-0.2.60/sandbox/src/components/ui/sheet.tsx +0 -137
- dulus-0.2.60/sandbox/src/components/ui/sidebar.tsx +0 -726
- dulus-0.2.60/sandbox/src/components/ui/skeleton.tsx +0 -13
- dulus-0.2.60/sandbox/src/components/ui/slider.tsx +0 -63
- dulus-0.2.60/sandbox/src/components/ui/sonner.tsx +0 -38
- dulus-0.2.60/sandbox/src/components/ui/spinner.tsx +0 -16
- dulus-0.2.60/sandbox/src/components/ui/switch.tsx +0 -31
- dulus-0.2.60/sandbox/src/components/ui/table.tsx +0 -114
- dulus-0.2.60/sandbox/src/components/ui/tabs.tsx +0 -66
- dulus-0.2.60/sandbox/src/components/ui/textarea.tsx +0 -18
- dulus-0.2.60/sandbox/src/components/ui/toggle-group.tsx +0 -81
- dulus-0.2.60/sandbox/src/components/ui/toggle.tsx +0 -45
- dulus-0.2.60/sandbox/src/components/ui/tooltip.tsx +0 -61
- dulus-0.2.60/sandbox/src/hooks/index.ts +0 -30
- dulus-0.2.60/sandbox/src/hooks/use-mobile.ts +0 -19
- dulus-0.2.60/sandbox/src/hooks/useAutoOpenChat.ts +0 -34
- dulus-0.2.60/sandbox/src/hooks/useDulusAgents.ts +0 -51
- dulus-0.2.60/sandbox/src/hooks/useDulusChat.ts +0 -73
- dulus-0.2.60/sandbox/src/hooks/useDulusEvents.ts +0 -116
- dulus-0.2.60/sandbox/src/hooks/useDulusHealth.ts +0 -52
- dulus-0.2.60/sandbox/src/hooks/useDulusMemory.ts +0 -78
- dulus-0.2.60/sandbox/src/hooks/useDulusSkills.ts +0 -60
- dulus-0.2.60/sandbox/src/hooks/useDulusTasks.ts +0 -90
- dulus-0.2.60/sandbox/src/hooks/useFileSystem.ts +0 -330
- dulus-0.2.60/sandbox/src/hooks/useOSStore.tsx +0 -553
- dulus-0.2.60/sandbox/src/hooks/useSkillBridge.ts +0 -72
- dulus-0.2.60/sandbox/src/hooks/useSystemBattery.ts +0 -77
- dulus-0.2.60/sandbox/src/hooks/useSystemNetwork.ts +0 -56
- dulus-0.2.60/sandbox/src/hooks/useSystemVolume.ts +0 -39
- dulus-0.2.60/sandbox/src/index.css +0 -169
- dulus-0.2.60/sandbox/src/lib/dulus-api.ts +0 -398
- dulus-0.2.60/sandbox/src/lib/utils.ts +0 -6
- dulus-0.2.60/sandbox/src/main.tsx +0 -5
- dulus-0.2.60/sandbox/src/types/index.ts +0 -252
- dulus-0.2.60/sandbox/src/utils/assets.ts +0 -15
- dulus-0.2.60/webbridge/__pycache__/__init__.cpython-314.pyc +0 -0
- dulus-0.2.60/webbridge/__pycache__/core.cpython-314.pyc +0 -0
- dulus-0.2.60/webbridge/__pycache__/tools.cpython-314.pyc +0 -0
- dulus-0.2.60/webbridge/tools.py +0 -261
- {dulus-0.2.60 → dulus-3.4.0}/LICENSE +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/backend/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/backend/context.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/backend/githook.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/backend/marketplace.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/backend/mempalace_bridge.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/backend/personas.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/backend/plugins.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/backend/server.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/backend/tasks.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/checkpoint/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/checkpoint/hooks.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/checkpoint/store.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/checkpoint/types.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/claude_code_watcher.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/clipboard_utils.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/cloudsave.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/common.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/data/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/data/active_persona.json +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/data/marketplace.json +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/data/personas.json +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/data/plugins/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/data/plugins/composio/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/data/plugins/composio/composio_plugin/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/data/plugins/composio/composio_plugin/session_manager.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/data/plugins/composio/plugin.json +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/data/tasks.json +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/azure-speech-template.json +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/divider.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/generate.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/nvidia-models.svg +0 -0
- {dulus-0.2.60/docs/uploads → dulus-3.4.0/docs}/particle-playground.html +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/personas/index.html +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/poetry-banner.png +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/sec-agents.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/sec-brainstorm.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/sec-bridges.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/sec-features.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/sec-freetier.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/sec-memory.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/sec-models.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/sec-perms.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/sec-plugins.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/sec-quickstart.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/sec-ssj.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/spinners.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/split-pane.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/docs/terminal-boot.svg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/dulus.egg-info/dependency_links.txt +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/dulus.egg-info/entry_points.txt +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/dulus_mcp/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/dulus_mcp/client.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/dulus_mcp/config.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/dulus_mcp/tools.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/dulus_mcp/types.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/gui/agent_bridge.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/gui/chat_widget.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/gui/personas.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/gui/session_utils.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/gui/settings_dialog.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/gui/sidebar.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/gui/tasks_view.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/gui/tool_panel.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/memory/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/memory/audit.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/memory/consolidator.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/memory/context.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/memory/offload.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/memory/palace.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/memory/scan.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/memory/sessions.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/memory/store.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/memory/tools.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/memory/types.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/memory/vector_search.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/multi_agent/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/multi_agent/subagent.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/multi_agent/tools.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/plugin/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/plugin/loader.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/plugin/recommend.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/plugin/store.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/plugin/types.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/README.md +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/components.json +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/dist/assets/index-DE51D6wI.css +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/dist/assets/index-DMCCNE9Y.js +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/dist/wallpaper-default.jpg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/dist/wallpapers/default.jpeg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/dist/wallpapers/light.jpeg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/dist/wallpapers/nature.jpeg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/dist/wallpapers/tech.jpeg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/eslint.config.js +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/index.html +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/info.md +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/package-lock.json +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/package.json +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/postcss.config.js +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/tailwind.config.js +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/tsconfig.app.json +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/tsconfig.json +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/tsconfig.node.json +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/sandbox/vite.config.ts +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/setup.cfg +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/skill/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/skill/builtin.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/skill/executor.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/skill/tools.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/skills.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/spinner.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/string_utils.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/subagent.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/task/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/task/store.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/task/tools.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/task/types.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_afk_yolo.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_approval_runtime.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_background_task_tools.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_background_tasks.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_checkpoint.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_clipboard_utils.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_compaction.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_diff_view.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_diff_visualization.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_display_blocks.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_export_import.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_hook_engine.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_injection_fix.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_license.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_mcp.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_memory.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_notification_manager.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_plugin.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_session_fork.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_shell_mode.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_skills.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_steer_input.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_subagent.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_task.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_telegram_buffer.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_think_tool.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_todo_tool.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_todo_visualization.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_tool_registry.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_voice.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tests/test_wire_events.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tmux_tools.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/tool_registry.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/ui/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/ui/input.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/ui/render.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/voice/__init__.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/voice/keyterms.py +0 -0
- {dulus-0.2.60 → dulus-3.4.0}/webchat.py +0 -0
dulus-3.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dulus
|
|
3
|
+
Version: 3.4.0
|
|
4
|
+
Summary: Use IA as an agent without an API key. $0. Harvest your browser session for Gemini (guest, no login), Claude.ai, Claude Code, Kimi.com, Qwen, DeepSeek — drive them like an API, no per-token billing. Plus LiteLLM (100+ paid providers), local models via Ollama, /lang in 34 languages, Mesa Redonda, voice, OCR, MemPalace, embedded sandbox OS.
|
|
5
|
+
Author: KevRojo
|
|
6
|
+
License: GPL-3.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/KevRojo/Dulus
|
|
8
|
+
Project-URL: Repository, https://github.com/KevRojo/Dulus
|
|
9
|
+
Project-URL: Issues, https://github.com/KevRojo/Dulus/issues
|
|
10
|
+
Keywords: ai,cli,llm,claude,gemini,nvidia,openai,kimi,deepseek,qwen,ollama,agent,tts,voice
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Terminals
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
License-File: license_manager.py
|
|
26
|
+
Requires-Dist: anthropic>=0.40.0
|
|
27
|
+
Requires-Dist: openai>=1.30.0
|
|
28
|
+
Requires-Dist: httpx>=0.27.0
|
|
29
|
+
Requires-Dist: requests>=2.31.0
|
|
30
|
+
Requires-Dist: rich>=13.0.0
|
|
31
|
+
Requires-Dist: prompt_toolkit>=3.0.0
|
|
32
|
+
Requires-Dist: Flask>=3.1.3
|
|
33
|
+
Requires-Dist: bubblewrap-cli>=1.0.0
|
|
34
|
+
Requires-Dist: customtkinter>=5.2.0
|
|
35
|
+
Requires-Dist: Pillow>=10.0.0
|
|
36
|
+
Requires-Dist: typing-extensions>=4.10.0
|
|
37
|
+
Requires-Dist: composio>=1.0.0rc2
|
|
38
|
+
Requires-Dist: beautifulsoup4>=4.12.0
|
|
39
|
+
Requires-Dist: sentry-sdk>=2.0.0
|
|
40
|
+
Requires-Dist: pytesseract>=0.3.10
|
|
41
|
+
Provides-Extra: memory
|
|
42
|
+
Requires-Dist: mempalace>=3.3.4; extra == "memory"
|
|
43
|
+
Provides-Extra: voice
|
|
44
|
+
Requires-Dist: sounddevice; extra == "voice"
|
|
45
|
+
Requires-Dist: elevenlabs>=1.0.0; extra == "voice"
|
|
46
|
+
Provides-Extra: webbridge
|
|
47
|
+
Requires-Dist: playwright>=1.40.0; extra == "webbridge"
|
|
48
|
+
Provides-Extra: litellm
|
|
49
|
+
Requires-Dist: litellm>=1.50.0; extra == "litellm"
|
|
50
|
+
Provides-Extra: ocr
|
|
51
|
+
Requires-Dist: pytesseract>=0.3.10; extra == "ocr"
|
|
52
|
+
Requires-Dist: Pillow>=10.0.0; extra == "ocr"
|
|
53
|
+
Provides-Extra: ocr-deep
|
|
54
|
+
Requires-Dist: pytesseract>=0.3.10; extra == "ocr-deep"
|
|
55
|
+
Requires-Dist: Pillow>=10.0.0; extra == "ocr-deep"
|
|
56
|
+
Requires-Dist: easyocr>=1.7.0; extra == "ocr-deep"
|
|
57
|
+
Provides-Extra: full
|
|
58
|
+
Requires-Dist: mempalace>=3.3.4; extra == "full"
|
|
59
|
+
Requires-Dist: sounddevice; extra == "full"
|
|
60
|
+
Requires-Dist: elevenlabs>=1.0.0; extra == "full"
|
|
61
|
+
Requires-Dist: playwright>=1.40.0; extra == "full"
|
|
62
|
+
Requires-Dist: litellm>=1.50.0; extra == "full"
|
|
63
|
+
Requires-Dist: pytesseract>=0.3.10; extra == "full"
|
|
64
|
+
Dynamic: license-file
|
|
65
|
+
|
|
66
|
+
# ▲ DULUS
|
|
67
|
+
>
|
|
68
|
+
> `pip install dulus` → press Enter → working IA in 30 seconds. Try it.
|
|
69
|
+
|
|
70
|
+
SET /sticky_input ON since the first run for the best experience!
|
|
71
|
+
|
|
72
|
+
<p align="center">
|
|
73
|
+
|
|
74
|
+
<img width="700" height="665" alt="mascot-hero" src="https://github.com/user-attachments/assets/8b122834-552c-4bd4-b46d-3e17deb74ab5" />
|
|
75
|
+
|
|
76
|
+
</p>
|
|
77
|
+
|
|
78
|
+
<p align="center"><i>The Dulus (Cigua Palmera) — Dominican national bird. Named after the bird, not the rocket.</i></p>
|
|
79
|
+
|
|
80
|
+
<p align="center">
|
|
81
|
+
<a href="#quick-start"><b>Quick Start</b></a> ·
|
|
82
|
+
<a href="#models"><b>Models</b></a> ·
|
|
83
|
+
<a href="#features"><b>Features</b></a> ·
|
|
84
|
+
<a href="#permissions"><b>Permissions</b></a> ·
|
|
85
|
+
<a href="#mcp"><b>MCP</b></a> ·
|
|
86
|
+
<a href="#plugins"><b>Plugins</b></a>
|
|
87
|
+
</p>
|
|
88
|
+
|
|
89
|
+
<p align="center">
|
|
90
|
+
<a href="https://pypi.org/project/dulus/"><img src="https://img.shields.io/pypi/v/dulus.svg?style=flat-square&color=ff6b1f&labelColor=07070a&label=pypi" alt="pypi"/></a>
|
|
91
|
+
<a href="https://pypi.org/project/dulus/"><img src="https://static.pepy.tech/badge/dulus?style=flat-square" alt="downloads"/></a>
|
|
92
|
+
<img src="https://img.shields.io/badge/python-3.11+-ff6b1f?style=flat-square&labelColor=07070a" alt="python"/>
|
|
93
|
+
<img src="https://img.shields.io/badge/license-GPLv3-ff6b1f?style=flat-square&labelColor=07070a" alt="license"/>
|
|
94
|
+
<img src="https://img.shields.io/badge/version-v3.3.3-ff6b1f?style=flat-square&labelColor=07070a" alt="version"/>
|
|
95
|
+
<img src="https://img.shields.io/badge/providers-100%2B%20via%20LiteLLM-ff6b1f?style=flat-square&labelColor=07070a" alt="providers"/>
|
|
96
|
+
<img src="https://img.shields.io/badge/tools-30%2B-ff6b1f?style=flat-square&labelColor=07070a" alt="tools"/>
|
|
97
|
+
<img src="https://img.shields.io/badge/tests-263+-ff6b1f?style=flat-square&labelColor=07070a" alt="tests"/>
|
|
98
|
+
<a href="https://x.com/KevRojo"><img src="https://img.shields.io/badge/x-%40KevRojo-ff6b1f?style=flat-square&labelColor=07070a&logo=x" alt="x"/></a>
|
|
99
|
+
</p>
|
|
100
|
+
|
|
101
|
+
<p align="center">
|
|
102
|
+
🌐 <b>Multilingual</b> ·
|
|
103
|
+
<a href="docs/README_EN.md">English</a> ·
|
|
104
|
+
<a href="docs/README_ES.md">Español</a> ·
|
|
105
|
+
<a href="docs/README_FR.md">Français</a> ·
|
|
106
|
+
<a href="docs/README_ZH.md">中文</a> ·
|
|
107
|
+
<a href="docs/README_JA.md">日本語</a> ·
|
|
108
|
+
<a href="docs/README_KO.md">한국어</a> ·
|
|
109
|
+
<a href="docs/README_PT.md">Português</a> ·
|
|
110
|
+
<a href="docs/README_RU.md">Русский</a> ·
|
|
111
|
+
<a href="docs/README_AR.md">العربية</a>
|
|
112
|
+
</p>
|
|
113
|
+
|
|
114
|
+
> **Official X / creator handle:** [@KevRojo](https://x.com/KevRojo) — that's me, the only contributor to this repo. Any other account claiming to be Dulus is a copycat.
|
|
115
|
+
>
|
|
116
|
+
> **$DULUS AI token Solana):** [`9R8rrjXxcfQPmLTCLhmVpjr2uesjjkcgkinE6Lwdpump`](https://dexscreener.com/solana/9R8rrjXxcfQPmLTCLhmVpjr2uesjjkcgkinE6Lwdpump)
|
|
117
|
+
|
|
118
|
+
-----
|
|
119
|
+
<p align="center">
|
|
120
|
+
<code>pip install dulus</code>
|
|
121
|
+
</p>
|
|
122
|
+
|
|
123
|
+
<p align="center"><img src="https://raw.githubusercontent.com/KevRojo/Dulus/main/docs/divider.svg" alt="" width="100%"></p>
|
|
124
|
+
|
|
125
|
+
<p align="center">
|
|
126
|
+
<a href="https://dulus.ai/"><b>🌐 Visit the Dulus website →</b></a><br>
|
|
127
|
+
<sub>The site covers features, demos, and details not documented in this README.</sub>
|
|
128
|
+
</p>
|
|
129
|
+
|
|
130
|
+
<p align="center"><img src="https://raw.githubusercontent.com/KevRojo/Dulus/main/docs/divider.svg" alt="" width="100%"></p>
|
|
131
|
+
|
|
132
|
+
## 🔥 What's new
|
|
133
|
+
|
|
134
|
+
> Full changelog: [`docs/news.md`](docs/news.md) · Inside the REPL: `/news`
|
|
135
|
+
|
|
136
|
+
- **Dulus Agent — Telegram communities.** Dulus evolved from a CLI tool into a live AI agent inside Telegram groups. The first fully autonomous AI moderating and conversing in real communities — not a bot, not a filter, a real agent. Groups pay in $DULUS to activate him. We host Dulus for them — that's the business model. The CLI stays free forever. This is the paid layer. 🦅🇩🇴
|
|
137
|
+
|
|
138
|
+
- **v0.2.93 — IA WITHOUT AN API KEY, FIRST-RUN.** 🦅 The welcome wizard now offers, by default, to open Gemini in a browser and capture its **guest session** — no Google login, no API key, no credit card. From `pip install` to working IA in 30 seconds. Same flow works for Claude.ai / Kimi.com / Qwen / DeepSeek if you have those accounts. This was Dulus's ace under the sleeve. Now it's the front door.
|
|
139
|
+
- **v0.2.92** — **`/lang` command.** 34 ISO codes + free-form descriptors. `/lang zh`, `/lang ja`, `/lang pt-br`, `/lang "speak as my gym tutor"`, `/lang "tigre de calle dominicano"`, `/lang "Yoda"`. The model role-plays the voice across the whole session. Triggered after Doubao (China's biggest IA assistant) started referring traffic to the repo.
|
|
140
|
+
- **v0.2.91** — **CORS on the daemon.** The Android Sandbox APK now connects every app live — same daemon, finally cross-origin reachable.
|
|
141
|
+
- **v0.2.89** — **Sandbox embedded INSIDE the desktop GUI.** Click `🦅 Sandbox` in the GUI Web tab → the Dulus OS renders inside the content frame via pywebview subprocess + Win32 `SetParent` reparent. Not a popup. Inside the frame.
|
|
142
|
+
- **v0.2.89** — **Local OCR shipped first-class.** `/ocr` + `ExtractTextFromImage` tool, `WebBridgeScreenshot` auto-OCRs, `/img` sends image + verbatim OCR text together. Zero vision-model tokens for receipts / code screenshots / error stacks.
|
|
143
|
+
- **v0.2.89** — **`kepano/obsidian-skills` bundled.** Dulus writes Obsidian-flavored markdown by default. Open `~/.dulus/memory/` as an Obsidian vault → instant graph view.
|
|
144
|
+
- **v0.2.88** — **LiteLLM gateway: one provider entry, 100+ backends.** OpenRouter, Groq, Together, Bedrock, Vertex, Cohere, Perplexity, xAI, Mistral, Fireworks, Anyscale, Replicate, Azure, DeepInfra — the welcome wizard auto-installs the package and asks for the right per-backend key.
|
|
145
|
+
- **v0.2.85** — **Slim wheel 11.4 MB → 2.5 MB.** Smaller than the original baseline.
|
|
146
|
+
|
|
147
|
+
<p align="center"><img src="https://raw.githubusercontent.com/KevRojo/Dulus/main/docs/divider.svg" alt="" width="100%"></p>
|
|
148
|
+
|
|
149
|
+
## What is this
|
|
150
|
+
|
|
151
|
+
Dulus Reduce your IA costs by 60% parsing webchats and claude-code directly. Write poetry while Anthropic only see text.
|
|
152
|
+
Use claude-code as an API without the new 'extra-usage' wall <3
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
Another reminder of a Dulus magic spell:
|
|
156
|
+
Wanna get stock prices, history , etc?
|
|
157
|
+
|
|
158
|
+
/plugin install yfinance@https://github.com/ranaroussi/yfinance
|
|
159
|
+
|
|
160
|
+
them:
|
|
161
|
+
/plugin reload
|
|
162
|
+
|
|
163
|
+
dulus get the prices of NVDA, TSLA, SP500:
|
|
164
|
+
|
|
165
|
+
Be creative!!!
|
|
166
|
+
|
|
167
|
+
Dulus adapt any python repository <3
|
dulus-3.4.0/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# ▲ DULUS
|
|
2
|
+
>
|
|
3
|
+
> `pip install dulus` → press Enter → working IA in 30 seconds. Try it.
|
|
4
|
+
|
|
5
|
+
SET /sticky_input ON since the first run for the best experience!
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
|
|
9
|
+
<img width="700" height="665" alt="mascot-hero" src="https://github.com/user-attachments/assets/8b122834-552c-4bd4-b46d-3e17deb74ab5" />
|
|
10
|
+
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center"><i>The Dulus (Cigua Palmera) — Dominican national bird. Named after the bird, not the rocket.</i></p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="#quick-start"><b>Quick Start</b></a> ·
|
|
17
|
+
<a href="#models"><b>Models</b></a> ·
|
|
18
|
+
<a href="#features"><b>Features</b></a> ·
|
|
19
|
+
<a href="#permissions"><b>Permissions</b></a> ·
|
|
20
|
+
<a href="#mcp"><b>MCP</b></a> ·
|
|
21
|
+
<a href="#plugins"><b>Plugins</b></a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<p align="center">
|
|
25
|
+
<a href="https://pypi.org/project/dulus/"><img src="https://img.shields.io/pypi/v/dulus.svg?style=flat-square&color=ff6b1f&labelColor=07070a&label=pypi" alt="pypi"/></a>
|
|
26
|
+
<a href="https://pypi.org/project/dulus/"><img src="https://static.pepy.tech/badge/dulus?style=flat-square" alt="downloads"/></a>
|
|
27
|
+
<img src="https://img.shields.io/badge/python-3.11+-ff6b1f?style=flat-square&labelColor=07070a" alt="python"/>
|
|
28
|
+
<img src="https://img.shields.io/badge/license-GPLv3-ff6b1f?style=flat-square&labelColor=07070a" alt="license"/>
|
|
29
|
+
<img src="https://img.shields.io/badge/version-v3.3.3-ff6b1f?style=flat-square&labelColor=07070a" alt="version"/>
|
|
30
|
+
<img src="https://img.shields.io/badge/providers-100%2B%20via%20LiteLLM-ff6b1f?style=flat-square&labelColor=07070a" alt="providers"/>
|
|
31
|
+
<img src="https://img.shields.io/badge/tools-30%2B-ff6b1f?style=flat-square&labelColor=07070a" alt="tools"/>
|
|
32
|
+
<img src="https://img.shields.io/badge/tests-263+-ff6b1f?style=flat-square&labelColor=07070a" alt="tests"/>
|
|
33
|
+
<a href="https://x.com/KevRojo"><img src="https://img.shields.io/badge/x-%40KevRojo-ff6b1f?style=flat-square&labelColor=07070a&logo=x" alt="x"/></a>
|
|
34
|
+
</p>
|
|
35
|
+
|
|
36
|
+
<p align="center">
|
|
37
|
+
🌐 <b>Multilingual</b> ·
|
|
38
|
+
<a href="docs/README_EN.md">English</a> ·
|
|
39
|
+
<a href="docs/README_ES.md">Español</a> ·
|
|
40
|
+
<a href="docs/README_FR.md">Français</a> ·
|
|
41
|
+
<a href="docs/README_ZH.md">中文</a> ·
|
|
42
|
+
<a href="docs/README_JA.md">日本語</a> ·
|
|
43
|
+
<a href="docs/README_KO.md">한국어</a> ·
|
|
44
|
+
<a href="docs/README_PT.md">Português</a> ·
|
|
45
|
+
<a href="docs/README_RU.md">Русский</a> ·
|
|
46
|
+
<a href="docs/README_AR.md">العربية</a>
|
|
47
|
+
</p>
|
|
48
|
+
|
|
49
|
+
> **Official X / creator handle:** [@KevRojo](https://x.com/KevRojo) — that's me, the only contributor to this repo. Any other account claiming to be Dulus is a copycat.
|
|
50
|
+
>
|
|
51
|
+
> **$DULUS AI token Solana):** [`9R8rrjXxcfQPmLTCLhmVpjr2uesjjkcgkinE6Lwdpump`](https://dexscreener.com/solana/9R8rrjXxcfQPmLTCLhmVpjr2uesjjkcgkinE6Lwdpump)
|
|
52
|
+
|
|
53
|
+
-----
|
|
54
|
+
<p align="center">
|
|
55
|
+
<code>pip install dulus</code>
|
|
56
|
+
</p>
|
|
57
|
+
|
|
58
|
+
<p align="center"><img src="https://raw.githubusercontent.com/KevRojo/Dulus/main/docs/divider.svg" alt="" width="100%"></p>
|
|
59
|
+
|
|
60
|
+
<p align="center">
|
|
61
|
+
<a href="https://dulus.ai/"><b>🌐 Visit the Dulus website →</b></a><br>
|
|
62
|
+
<sub>The site covers features, demos, and details not documented in this README.</sub>
|
|
63
|
+
</p>
|
|
64
|
+
|
|
65
|
+
<p align="center"><img src="https://raw.githubusercontent.com/KevRojo/Dulus/main/docs/divider.svg" alt="" width="100%"></p>
|
|
66
|
+
|
|
67
|
+
## 🔥 What's new
|
|
68
|
+
|
|
69
|
+
> Full changelog: [`docs/news.md`](docs/news.md) · Inside the REPL: `/news`
|
|
70
|
+
|
|
71
|
+
- **Dulus Agent — Telegram communities.** Dulus evolved from a CLI tool into a live AI agent inside Telegram groups. The first fully autonomous AI moderating and conversing in real communities — not a bot, not a filter, a real agent. Groups pay in $DULUS to activate him. We host Dulus for them — that's the business model. The CLI stays free forever. This is the paid layer. 🦅🇩🇴
|
|
72
|
+
|
|
73
|
+
- **v0.2.93 — IA WITHOUT AN API KEY, FIRST-RUN.** 🦅 The welcome wizard now offers, by default, to open Gemini in a browser and capture its **guest session** — no Google login, no API key, no credit card. From `pip install` to working IA in 30 seconds. Same flow works for Claude.ai / Kimi.com / Qwen / DeepSeek if you have those accounts. This was Dulus's ace under the sleeve. Now it's the front door.
|
|
74
|
+
- **v0.2.92** — **`/lang` command.** 34 ISO codes + free-form descriptors. `/lang zh`, `/lang ja`, `/lang pt-br`, `/lang "speak as my gym tutor"`, `/lang "tigre de calle dominicano"`, `/lang "Yoda"`. The model role-plays the voice across the whole session. Triggered after Doubao (China's biggest IA assistant) started referring traffic to the repo.
|
|
75
|
+
- **v0.2.91** — **CORS on the daemon.** The Android Sandbox APK now connects every app live — same daemon, finally cross-origin reachable.
|
|
76
|
+
- **v0.2.89** — **Sandbox embedded INSIDE the desktop GUI.** Click `🦅 Sandbox` in the GUI Web tab → the Dulus OS renders inside the content frame via pywebview subprocess + Win32 `SetParent` reparent. Not a popup. Inside the frame.
|
|
77
|
+
- **v0.2.89** — **Local OCR shipped first-class.** `/ocr` + `ExtractTextFromImage` tool, `WebBridgeScreenshot` auto-OCRs, `/img` sends image + verbatim OCR text together. Zero vision-model tokens for receipts / code screenshots / error stacks.
|
|
78
|
+
- **v0.2.89** — **`kepano/obsidian-skills` bundled.** Dulus writes Obsidian-flavored markdown by default. Open `~/.dulus/memory/` as an Obsidian vault → instant graph view.
|
|
79
|
+
- **v0.2.88** — **LiteLLM gateway: one provider entry, 100+ backends.** OpenRouter, Groq, Together, Bedrock, Vertex, Cohere, Perplexity, xAI, Mistral, Fireworks, Anyscale, Replicate, Azure, DeepInfra — the welcome wizard auto-installs the package and asks for the right per-backend key.
|
|
80
|
+
- **v0.2.85** — **Slim wheel 11.4 MB → 2.5 MB.** Smaller than the original baseline.
|
|
81
|
+
|
|
82
|
+
<p align="center"><img src="https://raw.githubusercontent.com/KevRojo/Dulus/main/docs/divider.svg" alt="" width="100%"></p>
|
|
83
|
+
|
|
84
|
+
## What is this
|
|
85
|
+
|
|
86
|
+
Dulus Reduce your IA costs by 60% parsing webchats and claude-code directly. Write poetry while Anthropic only see text.
|
|
87
|
+
Use claude-code as an API without the new 'extra-usage' wall <3
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
Another reminder of a Dulus magic spell:
|
|
91
|
+
Wanna get stock prices, history , etc?
|
|
92
|
+
|
|
93
|
+
/plugin install yfinance@https://github.com/ranaroussi/yfinance
|
|
94
|
+
|
|
95
|
+
them:
|
|
96
|
+
/plugin reload
|
|
97
|
+
|
|
98
|
+
dulus get the prices of NVDA, TSLA, SP500:
|
|
99
|
+
|
|
100
|
+
Be creative!!!
|
|
101
|
+
|
|
102
|
+
Dulus adapt any python repository <3
|
|
@@ -15,6 +15,7 @@ from tools import execute_tool
|
|
|
15
15
|
import tools as _tools_init # ensure built-in tools are registered on import
|
|
16
16
|
from providers import stream, AssistantTurn, TextChunk, ThinkingChunk, detect_provider
|
|
17
17
|
from compaction import maybe_compact
|
|
18
|
+
import governance
|
|
18
19
|
|
|
19
20
|
_SENTINEL = object()
|
|
20
21
|
|
|
@@ -116,6 +117,12 @@ def run(
|
|
|
116
117
|
pending_img = config.pop("_pending_image", None)
|
|
117
118
|
if pending_img:
|
|
118
119
|
user_msg["images"] = [pending_img]
|
|
120
|
+
# Attach pending video from /video command (Kimi K2.5/K2.6 only — the
|
|
121
|
+
# provider layer drops it for non-multimodal models). Item may be a bare
|
|
122
|
+
# base64 string or {"data": b64, "mime": "video/mp4"}.
|
|
123
|
+
pending_vid = config.pop("_pending_video", None)
|
|
124
|
+
if pending_vid:
|
|
125
|
+
user_msg["videos"] = [pending_vid]
|
|
119
126
|
|
|
120
127
|
initial_msg_count = len(state.messages)
|
|
121
128
|
state.messages.append(user_msg)
|
|
@@ -123,6 +130,12 @@ def run(
|
|
|
123
130
|
# Inject runtime metadata into config so tools (e.g. Agent) can access it
|
|
124
131
|
config.update({"_depth": depth, "_system_prompt": system_prompt})
|
|
125
132
|
|
|
133
|
+
# Governance layer (opt-in via config["governance"]). Built once per session
|
|
134
|
+
# and cached so the budget/ledger persists across turns. None when disabled.
|
|
135
|
+
if "_governance_obj" not in config:
|
|
136
|
+
config["_governance_obj"] = governance.from_config(config)
|
|
137
|
+
_gov = config["_governance_obj"]
|
|
138
|
+
|
|
126
139
|
while True:
|
|
127
140
|
if cancel_check and cancel_check():
|
|
128
141
|
return
|
|
@@ -184,6 +197,21 @@ def run(
|
|
|
184
197
|
cache_creation_tokens=c_create,
|
|
185
198
|
)
|
|
186
199
|
|
|
200
|
+
# Charge the token budget. On warn → notify; on first hard breach →
|
|
201
|
+
# fire on_breach and stop the loop gracefully (protects the wallet).
|
|
202
|
+
if _gov is not None and _gov.ledger is not None:
|
|
203
|
+
cr = _gov.ledger.charge("tokens", assistant_turn.in_tokens + assistant_turn.out_tokens)
|
|
204
|
+
if cr.warned and _gov.hooks:
|
|
205
|
+
_gov.hooks.fire("on_breach", kind="warn", dim=cr.dim, used=cr.used, granted=cr.granted)
|
|
206
|
+
if cr.first_breach:
|
|
207
|
+
if _gov.hooks:
|
|
208
|
+
_gov.hooks.fire("on_breach", kind="hard", dim=cr.dim, used=cr.used, granted=cr.granted)
|
|
209
|
+
yield TextChunk(
|
|
210
|
+
f"\n[governance] token budget reached ({cr.used}/{cr.granted}). "
|
|
211
|
+
"Stopping this run.\n"
|
|
212
|
+
)
|
|
213
|
+
break
|
|
214
|
+
|
|
187
215
|
if not assistant_turn.tool_calls:
|
|
188
216
|
break # No tools → conversation turn complete
|
|
189
217
|
|
|
@@ -191,6 +219,30 @@ def run(
|
|
|
191
219
|
for tc in assistant_turn.tool_calls:
|
|
192
220
|
yield ToolStart(tc["name"], tc["input"])
|
|
193
221
|
|
|
222
|
+
# ── Governance gate (capabilities + pre_tool hook) ───────────────
|
|
223
|
+
# Runs BEFORE the normal permission gate. A capability denial or a
|
|
224
|
+
# vetoing pre_tool hook short-circuits the tool with a recorded
|
|
225
|
+
# denial, so the model sees why and can adapt.
|
|
226
|
+
if _gov is not None:
|
|
227
|
+
_gov_deny = None
|
|
228
|
+
if _gov.capabilities is not None and not _gov.capabilities.allows_tool(tc["name"]):
|
|
229
|
+
_gov_deny = f"tool '{tc['name']}' not permitted by capability policy"
|
|
230
|
+
if _gov_deny is None and _gov.hooks is not None:
|
|
231
|
+
_ok, _why = _gov.hooks.fire(
|
|
232
|
+
"pre_tool", name=tc["name"], inputs=tc["input"], depth=depth)
|
|
233
|
+
if not _ok:
|
|
234
|
+
_gov_deny = _why
|
|
235
|
+
if _gov_deny is not None:
|
|
236
|
+
result = f"Denied by governance: {_gov_deny}"
|
|
237
|
+
if _gov.ledger is not None:
|
|
238
|
+
_gov.ledger.charge("tool_calls", 1)
|
|
239
|
+
yield ToolEnd(tc["name"], result, False)
|
|
240
|
+
state.messages.append({
|
|
241
|
+
"role": "tool", "tool_call_id": tc["id"],
|
|
242
|
+
"name": tc["name"], "content": result,
|
|
243
|
+
})
|
|
244
|
+
continue
|
|
245
|
+
|
|
194
246
|
# Permission gate
|
|
195
247
|
permitted = _check_permission(tc, config)
|
|
196
248
|
if not permitted:
|
|
@@ -223,6 +275,15 @@ def run(
|
|
|
223
275
|
|
|
224
276
|
yield ToolEnd(tc["name"], result, permitted)
|
|
225
277
|
|
|
278
|
+
# Governance: charge the tool-call budget + fire post_tool hook
|
|
279
|
+
# (observational — audit, notify, metrics). Never blocks here.
|
|
280
|
+
if _gov is not None:
|
|
281
|
+
if _gov.ledger is not None:
|
|
282
|
+
_gov.ledger.charge("tool_calls", 1)
|
|
283
|
+
if _gov.hooks is not None:
|
|
284
|
+
_gov.hooks.fire("post_tool", name=tc["name"], inputs=tc["input"],
|
|
285
|
+
result=result, permitted=permitted, depth=depth)
|
|
286
|
+
|
|
226
287
|
# Determine what the USER actually saw rendered, based on tool type +
|
|
227
288
|
# auto_show + verbose. Inject a SYSTEM HINT when user saw nothing useful,
|
|
228
289
|
# so the model can decide whether to PrintToConsole the content.
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"""Agent info bridge — transforms personas / sub-agent tasks into AgentInfo format."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
# We deliberately AVOID importing backend.context.build_context here —
|
|
5
|
+
# it walks the entire source tree with rglob() and tries to git/write into
|
|
6
|
+
# the package directory, both of which break when Dulus is pip-installed
|
|
7
|
+
# (the package lives in read-only site-packages, not a git repo). Calling
|
|
8
|
+
# the lighter get_personas_for_context directly gives the AgentMonitor app
|
|
9
|
+
# what it needs without the rglob/git crash.
|
|
5
10
|
|
|
6
11
|
|
|
7
12
|
def build_agent_info_list() -> list[dict]:
|
|
@@ -40,8 +45,8 @@ def build_agent_info_list() -> list[dict]:
|
|
|
40
45
|
|
|
41
46
|
# 2. Fallback / supplement with personas so the UI isn't empty
|
|
42
47
|
try:
|
|
43
|
-
|
|
44
|
-
persona_agents =
|
|
48
|
+
from backend.personas import get_personas_for_context
|
|
49
|
+
persona_agents = get_personas_for_context()
|
|
45
50
|
existing_ids = {a["id"] for a in agents}
|
|
46
51
|
for p in persona_agents:
|
|
47
52
|
pid = p.get("name", "unknown").lower().replace(" ", "-")
|
|
@@ -244,7 +244,7 @@ def summarize_memory(name: str, body: str) -> str:
|
|
|
244
244
|
if __name__ == "__main__":
|
|
245
245
|
sample = (
|
|
246
246
|
"[DULUS CONTEXT]\n"
|
|
247
|
-
"Session: proactive | Agent: Dulus | User:
|
|
247
|
+
"Session: proactive | Agent: Dulus | User: <user>\n" # demo sample only
|
|
248
248
|
"Project: Dulus Command Center | Files: 11 | Lines: 2014\n"
|
|
249
249
|
"Active Tasks:\n"
|
|
250
250
|
" • T-002 [in_progress] Smart Context Manager (#23) (Dulus, Core)\n"
|
|
@@ -171,6 +171,171 @@ class BatchManager:
|
|
|
171
171
|
KimiBatchManager = BatchManager # old name still works
|
|
172
172
|
|
|
173
173
|
|
|
174
|
+
# ── Anthropic Batch Manager ──────────────────────────────────────────────────
|
|
175
|
+
#
|
|
176
|
+
# Anthropic batch API is conceptually identical to OpenAI's but the wire format
|
|
177
|
+
# differs:
|
|
178
|
+
# - Requests are sent INLINE in a single create() call (no JSONL upload step).
|
|
179
|
+
# - The key under each item is "params" (not "body").
|
|
180
|
+
# - Status is "processing_status" with values: in_progress | canceling | ended.
|
|
181
|
+
# - Results stream as individual JSONL records, accessible via .results(id).
|
|
182
|
+
#
|
|
183
|
+
# 50% discount on both input and output tokens. Up to 24h SLA (usually <1h).
|
|
184
|
+
|
|
185
|
+
class AnthropicBatchManager:
|
|
186
|
+
"""Manager for Anthropic's native batch API (claude messages).
|
|
187
|
+
|
|
188
|
+
Same surface as BatchManager (prepare / create / retrieve / results /
|
|
189
|
+
cancel) so the rest of dulus can treat it interchangeably.
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
DEFAULT_MODEL = "claude-haiku-4-5"
|
|
193
|
+
|
|
194
|
+
def __init__(self, api_key: str):
|
|
195
|
+
try:
|
|
196
|
+
import anthropic # noqa: F401
|
|
197
|
+
except ImportError as e:
|
|
198
|
+
raise RuntimeError(
|
|
199
|
+
"anthropic SDK not installed (pip install anthropic)"
|
|
200
|
+
) from e
|
|
201
|
+
import anthropic as _ant
|
|
202
|
+
self.api_key = api_key
|
|
203
|
+
self.client = _ant.Anthropic(api_key=api_key)
|
|
204
|
+
|
|
205
|
+
def prepare_requests(
|
|
206
|
+
self,
|
|
207
|
+
prompts: List[str],
|
|
208
|
+
model: str = None,
|
|
209
|
+
system_prompt: str = None,
|
|
210
|
+
max_tokens: int = 1024,
|
|
211
|
+
) -> List[Dict[str, Any]]:
|
|
212
|
+
"""Build the requests array for batches.create()."""
|
|
213
|
+
if model is None:
|
|
214
|
+
model = self.DEFAULT_MODEL
|
|
215
|
+
if system_prompt is None:
|
|
216
|
+
system_prompt = BATCH_SYSTEM_PROMPT
|
|
217
|
+
|
|
218
|
+
ts = int(time.time())
|
|
219
|
+
out: List[Dict[str, Any]] = []
|
|
220
|
+
for i, prompt in enumerate(prompts):
|
|
221
|
+
params: Dict[str, Any] = {
|
|
222
|
+
"model": model,
|
|
223
|
+
"max_tokens": max_tokens,
|
|
224
|
+
"messages": [{"role": "user", "content": prompt}],
|
|
225
|
+
}
|
|
226
|
+
if system_prompt:
|
|
227
|
+
params["system"] = system_prompt
|
|
228
|
+
out.append({
|
|
229
|
+
"custom_id": f"req_{ts}_{i}",
|
|
230
|
+
"params": params,
|
|
231
|
+
})
|
|
232
|
+
return out
|
|
233
|
+
|
|
234
|
+
def create_batch(self, requests: List[Dict[str, Any]]) -> str:
|
|
235
|
+
"""Create a batch inline. Returns batch_id."""
|
|
236
|
+
batch = self.client.messages.batches.create(requests=requests)
|
|
237
|
+
return batch.id
|
|
238
|
+
|
|
239
|
+
def retrieve_batch(self, batch_id: str) -> Dict[str, Any]:
|
|
240
|
+
"""Get batch status. Normalizes field names to match BatchManager."""
|
|
241
|
+
b = self.client.messages.batches.retrieve(batch_id)
|
|
242
|
+
proc = getattr(b, "processing_status", None)
|
|
243
|
+
status = {
|
|
244
|
+
"in_progress": "in_progress",
|
|
245
|
+
"canceling": "cancelling",
|
|
246
|
+
"ended": "completed",
|
|
247
|
+
}.get(proc, proc or "unknown")
|
|
248
|
+
|
|
249
|
+
counts_raw = getattr(b, "request_counts", None)
|
|
250
|
+
counts: Dict[str, int] = {}
|
|
251
|
+
if counts_raw is not None:
|
|
252
|
+
for k in ("processing", "succeeded", "errored",
|
|
253
|
+
"canceled", "expired"):
|
|
254
|
+
v = getattr(counts_raw, k, 0)
|
|
255
|
+
if v:
|
|
256
|
+
counts[k] = v
|
|
257
|
+
counts["completed"] = counts.get("succeeded", 0)
|
|
258
|
+
counts["total"] = sum([
|
|
259
|
+
counts.get("processing", 0),
|
|
260
|
+
counts.get("succeeded", 0),
|
|
261
|
+
counts.get("errored", 0),
|
|
262
|
+
counts.get("canceled", 0),
|
|
263
|
+
counts.get("expired", 0),
|
|
264
|
+
])
|
|
265
|
+
|
|
266
|
+
if status == "completed" and counts:
|
|
267
|
+
if counts.get("errored", 0) and not counts.get("succeeded", 0):
|
|
268
|
+
status = "failed"
|
|
269
|
+
elif counts.get("expired", 0) and not counts.get("succeeded", 0):
|
|
270
|
+
status = "expired"
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
"id": b.id,
|
|
274
|
+
"status": status,
|
|
275
|
+
"processing_status": proc,
|
|
276
|
+
"request_counts": counts,
|
|
277
|
+
"output_file_id": None,
|
|
278
|
+
"results_url": getattr(b, "results_url", None),
|
|
279
|
+
"created_at": str(getattr(b, "created_at", "")),
|
|
280
|
+
"ended_at": str(getattr(b, "ended_at", "") or ""),
|
|
281
|
+
"expires_at": str(getattr(b, "expires_at", "") or ""),
|
|
282
|
+
"completed_at": str(getattr(b, "ended_at", "") or ""),
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
def cancel_batch(self, batch_id: str) -> Dict[str, Any]:
|
|
286
|
+
"""Cancel a running batch."""
|
|
287
|
+
b = self.client.messages.batches.cancel(batch_id)
|
|
288
|
+
return {"id": b.id, "status": getattr(b, "processing_status", "unknown")}
|
|
289
|
+
|
|
290
|
+
def results(self, batch_id: str) -> List[Dict[str, Any]]:
|
|
291
|
+
"""Fetch all results for a completed batch.
|
|
292
|
+
|
|
293
|
+
Returns: [{custom_id, type, text, error?, usage}]
|
|
294
|
+
where type in {succeeded, errored, canceled, expired}
|
|
295
|
+
"""
|
|
296
|
+
out: List[Dict[str, Any]] = []
|
|
297
|
+
for r in self.client.messages.batches.results(batch_id):
|
|
298
|
+
entry: Dict[str, Any] = {
|
|
299
|
+
"custom_id": getattr(r, "custom_id", None),
|
|
300
|
+
"type": None,
|
|
301
|
+
"text": "",
|
|
302
|
+
"error": None,
|
|
303
|
+
"usage": None,
|
|
304
|
+
}
|
|
305
|
+
res = getattr(r, "result", None)
|
|
306
|
+
if res is None:
|
|
307
|
+
out.append(entry); continue
|
|
308
|
+
t = getattr(res, "type", None)
|
|
309
|
+
entry["type"] = t
|
|
310
|
+
if t == "succeeded":
|
|
311
|
+
msg = getattr(res, "message", None)
|
|
312
|
+
if msg:
|
|
313
|
+
parts = []
|
|
314
|
+
for block in getattr(msg, "content", []) or []:
|
|
315
|
+
if getattr(block, "type", None) == "text":
|
|
316
|
+
parts.append(getattr(block, "text", "") or "")
|
|
317
|
+
entry["text"] = "\n".join(parts).strip()
|
|
318
|
+
u = getattr(msg, "usage", None)
|
|
319
|
+
if u:
|
|
320
|
+
entry["usage"] = {
|
|
321
|
+
"input_tokens": getattr(u, "input_tokens", 0),
|
|
322
|
+
"output_tokens": getattr(u, "output_tokens", 0),
|
|
323
|
+
"cache_read_input_tokens":
|
|
324
|
+
getattr(u, "cache_read_input_tokens", 0),
|
|
325
|
+
"cache_creation_input_tokens":
|
|
326
|
+
getattr(u, "cache_creation_input_tokens", 0),
|
|
327
|
+
}
|
|
328
|
+
elif t == "errored":
|
|
329
|
+
err_obj = getattr(res, "error", None)
|
|
330
|
+
entry["error"] = (
|
|
331
|
+
{"type": getattr(err_obj, "type", None),
|
|
332
|
+
"message": getattr(err_obj, "message", None)}
|
|
333
|
+
if err_obj else "unknown_error"
|
|
334
|
+
)
|
|
335
|
+
out.append(entry)
|
|
336
|
+
return out
|
|
337
|
+
|
|
338
|
+
|
|
174
339
|
# ── Local job persistence ────────────────────────────────────────────────────
|
|
175
340
|
|
|
176
341
|
_JOBS_DIR = os.path.join(os.path.expanduser("~"), ".dulus", "jobs")
|