hellm 0.0.1
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.
- checksums.yaml +7 -0
- data/.github/agents/developer.md +38 -0
- data/.github/skills/ruby/SKILL.md +30 -0
- data/.github/skills/test/SKILL.md +67 -0
- data/ARCHITECTURE.md +183 -0
- data/LICENSE.txt +21 -0
- data/README.md +88 -0
- data/Rakefile +13 -0
- data/VERSION +1 -0
- data/example/.hellm/agents/alfred.agent.md +8 -0
- data/example/.hellm/agents/mathematician.agent.md +8 -0
- data/example/.hellm/skills/farben/SKILL.md +20 -0
- data/example/example.sh +3 -0
- data/lib/hellm/agent.rb +142 -0
- data/lib/hellm/builder.rb +86 -0
- data/lib/hellm/markdown_file.rb +53 -0
- data/lib/hellm/skill.rb +11 -0
- data/lib/hellm/stream_adapter.rb +7 -0
- data/lib/hellm/tools/agent_tool_factory.rb +34 -0
- data/lib/hellm/tools/registry.rb +53 -0
- data/lib/hellm/tools/skill_tool.rb +29 -0
- data/lib/hellm/version.rb +5 -0
- data/lib/hellm.rb +39 -0
- data/sig/hellm.rbs +4 -0
- metadata +139 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 75af48a7436c71030b39ada0b3acd8a74919c187816d3685715801f70bce314b
|
|
4
|
+
data.tar.gz: 11110f4770b2b25fb4ac92a46aff9d20b1c41a26abdf0d5e0790e7e0a698be9d
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 958c4ccb58b11db9e67168e7cd4e1a667be90ddcc921a7585a3f40a50c0a83d36c3def47b79fa4988e61f0ec39749edf9efc7d8421f2e70b9909f97d79d9d972
|
|
7
|
+
data.tar.gz: ad4474e9e0a64c4c9ff2c4687e3bf0c58ed40eaaf3078e42e8443ec8f62f8b6654625e4004218d0856e3c100a90e9d6ceafb791d3ba9de50255f850ca149bffb
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: developer
|
|
3
|
+
description: "Development agent for Hellm. Use for ruby and rspec changes."
|
|
4
|
+
tools: [read, search, edit, execute, todo]
|
|
5
|
+
user-invocable: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are a Ruby development agent.
|
|
9
|
+
|
|
10
|
+
## First Context To Load
|
|
11
|
+
|
|
12
|
+
Before proposing or editing code, read:
|
|
13
|
+
|
|
14
|
+
1. `ARCHITECTURE.md`
|
|
15
|
+
|
|
16
|
+
## Project-Specific Constraints
|
|
17
|
+
|
|
18
|
+
- Language throughout code and documentation is English.
|
|
19
|
+
- Keep domain logic inside existing namespaces (`Hellm`, `Hellm::Tools`) unless a new boundary is clearly justified.
|
|
20
|
+
|
|
21
|
+
## Preferred Approach
|
|
22
|
+
|
|
23
|
+
1. Identify impacted module and classes.
|
|
24
|
+
2. Implement smallest coherent change, preserving existing APIs unless change request demands otherwise.
|
|
25
|
+
3. Add or update tests to cover new behavior and prevent regressions.
|
|
26
|
+
4. Summarize architecture impact in the final response, including code coverage report and any risks or missing tests.
|
|
27
|
+
5. Update `ARCHITECTURE.md` if the change introduces new patterns or behaviors.
|
|
28
|
+
|
|
29
|
+
## Skill Selection
|
|
30
|
+
|
|
31
|
+
- Use the `ruby` skill for language-level style, refactors, and Ruby object patterns.
|
|
32
|
+
- Use the `test` skill for writing and debugging specs with RSpec.
|
|
33
|
+
|
|
34
|
+
## Output Requirements
|
|
35
|
+
|
|
36
|
+
- Explain what changed and why it matches existing project patterns.
|
|
37
|
+
- Call out risks, regressions, or missing tests when relevant.
|
|
38
|
+
- Keep recommendations grounded in `ARCHITECTURE.md` and current code behavior.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ruby
|
|
3
|
+
description: "Ruby implementation skill. Use for idiomatic Ruby code changes."
|
|
4
|
+
argument-hint: "Describe the Ruby behavior to implement or refactor, including affected modules or classes."
|
|
5
|
+
user-invocable: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Ruby Skill For Arealsans
|
|
9
|
+
|
|
10
|
+
Use this skill when the task is mostly Ruby-level logic.
|
|
11
|
+
|
|
12
|
+
## Load Context First
|
|
13
|
+
|
|
14
|
+
1. Read `ARCHITECTURE.md`.
|
|
15
|
+
2. Read the concrete module or class files touched by the request.
|
|
16
|
+
|
|
17
|
+
## Repository Ruby Conventions
|
|
18
|
+
|
|
19
|
+
- Prefer namespace-scoped classes/modules (`Hellm::...`, `Hellm::Tools::...`).
|
|
20
|
+
- Avoid introducing unrelated abstractions when a focused method-level change is sufficient.
|
|
21
|
+
- Private methods are only refactored if it improves clarity or reduces duplication.
|
|
22
|
+
|
|
23
|
+
## Procedure
|
|
24
|
+
|
|
25
|
+
1. Implement the minimum Ruby change that satisfies the request.
|
|
26
|
+
2. Keep method names and signatures consistent unless the change requires API updates.
|
|
27
|
+
|
|
28
|
+
## Quality Checklist
|
|
29
|
+
|
|
30
|
+
- Public method output remains compatible with current callers.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test
|
|
3
|
+
description: "Testing skill. Use for writing and debugging specs with RSpec."
|
|
4
|
+
argument-hint: "Describe what behavior should be tested, including context and expected outcomes."
|
|
5
|
+
user-invocable: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Testing Skill For Arealsans
|
|
9
|
+
|
|
10
|
+
Use this skill when implementing or fixing automated tests in this project.
|
|
11
|
+
|
|
12
|
+
## Test Stack
|
|
13
|
+
|
|
14
|
+
- RSpec
|
|
15
|
+
|
|
16
|
+
## Mandatory Context
|
|
17
|
+
|
|
18
|
+
1. Read `ARCHITECTURE.md` first.
|
|
19
|
+
2. Read existing nearby specs to mirror conventions before adding new ones.
|
|
20
|
+
|
|
21
|
+
## When To Use
|
|
22
|
+
|
|
23
|
+
- Add coverage for new behavior
|
|
24
|
+
- Reproduce and prevent regressions
|
|
25
|
+
|
|
26
|
+
## Procedure
|
|
27
|
+
|
|
28
|
+
1. Identify the thinnest test that proves behavior.
|
|
29
|
+
2. Run focused specs first, then a wider relevant subset.
|
|
30
|
+
|
|
31
|
+
## Repository Guardrails
|
|
32
|
+
|
|
33
|
+
- Prefer clear setup helpers (before/after) over deeply nested let chains.
|
|
34
|
+
- Validate both success and failure paths.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## Suggested Commands
|
|
38
|
+
|
|
39
|
+
- `bundle exec rspec spec/..._spec.rb`
|
|
40
|
+
- `bundle exec rspec --profile 10`
|
|
41
|
+
|
|
42
|
+
## Code Coverage (SimpleCov)
|
|
43
|
+
|
|
44
|
+
- Use SimpleCov for all RSpec runs and keep project-wide minimum coverage at 80%.
|
|
45
|
+
- If coverage drops below 80%, tests should fail and coverage must be restored before merge.
|
|
46
|
+
- Prefer meaningful behavioral tests over coverage-only assertions.
|
|
47
|
+
- Output of SimpleCov should be reviewed to identify untested code paths and guide new test additions.
|
|
48
|
+
- SimpleCov should be configured to exclude irrelevant files and focus on application code.
|
|
49
|
+
- Output of SimpleCov should be written to directory `log/coverage` and include HTML reports for local review.
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
Example baseline configuration (typically in `spec/spec_helper.rb`):
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
require "simplecov"
|
|
56
|
+
|
|
57
|
+
SimpleCov.start "hellm" do
|
|
58
|
+
minimum_coverage 80
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Done Criteria
|
|
63
|
+
|
|
64
|
+
- New behavior is covered at the right layer.
|
|
65
|
+
- Regression path has a failing-to-passing spec story.
|
|
66
|
+
- SimpleCov project coverage is at least 80%.
|
|
67
|
+
- Specs are readable, deterministic, and avoid timing flakiness.
|
data/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Hellm Architecture Overview
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Hellm is a Ruby gem that builds and runs Langchain-based AI agents from project-local markdown configuration.
|
|
6
|
+
|
|
7
|
+
The framework focuses on:
|
|
8
|
+
- Declarative agent setup in markdown files
|
|
9
|
+
- Runtime composition of tools and sub-agent calls
|
|
10
|
+
- Skill injection from markdown documents
|
|
11
|
+
- A thin CLI wrapper for local execution
|
|
12
|
+
|
|
13
|
+
## High-Level Structure
|
|
14
|
+
|
|
15
|
+
Main source folders:
|
|
16
|
+
- lib/hellm.rb: Public entrypoints and global configuration
|
|
17
|
+
- lib/hellm/: Core runtime classes
|
|
18
|
+
- lib/hellm/tools/: Tool registry and dynamic tool builders
|
|
19
|
+
- bin/hellm: Command-line runner
|
|
20
|
+
- example/: Sample .hellm project with agents and skills
|
|
21
|
+
|
|
22
|
+
## Runtime Flow
|
|
23
|
+
|
|
24
|
+
```mermaid
|
|
25
|
+
flowchart TD
|
|
26
|
+
A[CLI: bin/hellm] --> B[Hellm.build_agent]
|
|
27
|
+
B --> C[Hellm::Builder]
|
|
28
|
+
C --> D[Load agents from .hellm/agents]
|
|
29
|
+
C --> E[Load skills from .hellm/skills/**/SKILL.md]
|
|
30
|
+
C --> F[Register builtin tools in Registry]
|
|
31
|
+
C --> G[Register skill tool in Registry]
|
|
32
|
+
C --> H[Register sub-agent tools in Registry]
|
|
33
|
+
H --> I[Hellm::Agent selected by name]
|
|
34
|
+
I --> J[agent.add content/image]
|
|
35
|
+
J --> K[Langchain::Assistant.run!]
|
|
36
|
+
K --> L[Tool calls via Registry instances]
|
|
37
|
+
K --> M[Response returned by agent.response]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Core Components
|
|
41
|
+
|
|
42
|
+
### Hellm module
|
|
43
|
+
|
|
44
|
+
Location: lib/hellm.rb
|
|
45
|
+
|
|
46
|
+
Responsibilities:
|
|
47
|
+
- Stores global API key fallback from OPENAI_API_KEY
|
|
48
|
+
- Builds an agent through Hellm::Builder
|
|
49
|
+
- Exposes logger and log-level integration with Langchain
|
|
50
|
+
|
|
51
|
+
Design notes:
|
|
52
|
+
- Global mutable state is limited to logger and API key.
|
|
53
|
+
- Agent construction delegates to Builder to keep orchestration out of the top-level API.
|
|
54
|
+
|
|
55
|
+
### Hellm::Builder
|
|
56
|
+
|
|
57
|
+
Location: lib/hellm/builder.rb
|
|
58
|
+
|
|
59
|
+
Responsibilities:
|
|
60
|
+
- Resolves configuration root (.hellm preferred, .github fallback)
|
|
61
|
+
- Loads agent definitions from markdown files under agents/
|
|
62
|
+
- Loads skill definitions from skills/**/SKILL.md
|
|
63
|
+
- Registers tools in Hellm::Tools::Registry
|
|
64
|
+
- Returns the selected runnable Hellm::Agent
|
|
65
|
+
|
|
66
|
+
Behavior details:
|
|
67
|
+
- Raises ArgumentError if no .hellm or .github directory exists.
|
|
68
|
+
- Always appends the skill tool to each configured agent.
|
|
69
|
+
- Registers built-in calculator tool by default.
|
|
70
|
+
|
|
71
|
+
### Hellm::Agent
|
|
72
|
+
|
|
73
|
+
Location: lib/hellm/agent.rb
|
|
74
|
+
|
|
75
|
+
Responsibilities:
|
|
76
|
+
- Wraps Langchain::Assistant lifecycle and message exchange
|
|
77
|
+
- Converts configured tool names to tool instances via Registry
|
|
78
|
+
- Adds text and image input messages
|
|
79
|
+
- Streams callbacks through StreamAdapter
|
|
80
|
+
- Executes with retry logic for transient rate-limit errors
|
|
81
|
+
|
|
82
|
+
Behavior details:
|
|
83
|
+
- Default model is gpt-5-nano.
|
|
84
|
+
- Removes literal agent tool token from tools list and relies on explicit sub-agent wiring.
|
|
85
|
+
- Supports image input as URL, data URL, or local file path converted to base64 data URL.
|
|
86
|
+
|
|
87
|
+
### Hellm::MarkdownFile
|
|
88
|
+
|
|
89
|
+
Location: lib/hellm/markdown_file.rb
|
|
90
|
+
|
|
91
|
+
Responsibilities:
|
|
92
|
+
- Reads markdown files with YAML frontmatter
|
|
93
|
+
- Splits frontmatter and content
|
|
94
|
+
- Exposes typed fetch access for attributes
|
|
95
|
+
|
|
96
|
+
### Hellm::Skill
|
|
97
|
+
|
|
98
|
+
Location: lib/hellm/skill.rb
|
|
99
|
+
|
|
100
|
+
Responsibilities:
|
|
101
|
+
- Value object representing skill metadata and markdown content
|
|
102
|
+
|
|
103
|
+
### Hellm::StreamAdapter
|
|
104
|
+
|
|
105
|
+
Location: lib/hellm/stream_adapter.rb
|
|
106
|
+
|
|
107
|
+
Responsibilities:
|
|
108
|
+
- Callback interface for streamed assistant messages and tool calls
|
|
109
|
+
- Base implementation is intentionally empty and intended for customization
|
|
110
|
+
|
|
111
|
+
## Tooling Architecture
|
|
112
|
+
|
|
113
|
+
### Hellm::Tools::Registry
|
|
114
|
+
|
|
115
|
+
Location: lib/hellm/tools/registry.rb
|
|
116
|
+
|
|
117
|
+
Responsibilities:
|
|
118
|
+
- Singleton registry for named tools
|
|
119
|
+
- Supports factory or fixed-instance registration
|
|
120
|
+
- Lazy-instantiates tools from factories and reuses instances
|
|
121
|
+
- Creates and registers sub-agent tools
|
|
122
|
+
|
|
123
|
+
Key implications:
|
|
124
|
+
- Tool instances are shared after first creation.
|
|
125
|
+
- Custom tools should avoid mutable request-specific state.
|
|
126
|
+
|
|
127
|
+
### Hellm::Tools::SkillTool
|
|
128
|
+
|
|
129
|
+
Location: lib/hellm/tools/skill_tool.rb
|
|
130
|
+
|
|
131
|
+
Responsibilities:
|
|
132
|
+
- Dynamically defines tool functions for each loaded skill
|
|
133
|
+
- Returns raw skill content when called by the LLM
|
|
134
|
+
|
|
135
|
+
### Hellm::Tools::AgentToolFactory
|
|
136
|
+
|
|
137
|
+
Location: lib/hellm/tools/agent_tool_factory.rb
|
|
138
|
+
|
|
139
|
+
Responsibilities:
|
|
140
|
+
- Builds dynamic Langchain tools that proxy calls into sub-agents
|
|
141
|
+
- Starts a fresh sub-agent session per invocation
|
|
142
|
+
|
|
143
|
+
## Configuration Model
|
|
144
|
+
|
|
145
|
+
Project configuration is stored in a `.hellm` directory with the following structure:
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
.hellm/
|
|
149
|
+
|-- agents/
|
|
150
|
+
| `-- *.md
|
|
151
|
+
| |-- frontmatter: model, name, description, tools, agents
|
|
152
|
+
| `-- body: agent instructions
|
|
153
|
+
`-- skills/
|
|
154
|
+
`-- **/
|
|
155
|
+
`-- SKILL.md
|
|
156
|
+
|-- frontmatter: name, description, argument-hint
|
|
157
|
+
`-- body: skill text returned by the generated skill tool
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Extension Points
|
|
161
|
+
|
|
162
|
+
Supported extension seams:
|
|
163
|
+
- Register additional tools via Hellm::Tools::Registry.instance.add
|
|
164
|
+
- Add new agents by placing markdown files in .hellm/agents
|
|
165
|
+
- Add new skills by placing SKILL.md files in .hellm/skills
|
|
166
|
+
- Provide a custom StreamAdapter implementation for telemetry or UI streaming
|
|
167
|
+
|
|
168
|
+
## Known Risks And Gaps
|
|
169
|
+
|
|
170
|
+
Current technical risks:
|
|
171
|
+
- Registry is process-global singleton with shared tool instances; stateful tools can leak behavior across sessions.
|
|
172
|
+
- Minimal validation of markdown frontmatter means malformed config errors may surface late.
|
|
173
|
+
|
|
174
|
+
Testing status:
|
|
175
|
+
- RSpec covers core behavior for Builder, Registry, tools, and LLM-free Agent logic.
|
|
176
|
+
- SimpleCov runs with the suite and writes coverage output to log/coverage.
|
|
177
|
+
|
|
178
|
+
## Future Hardening Priorities
|
|
179
|
+
|
|
180
|
+
Recommended next steps:
|
|
181
|
+
- Add integration specs for builder loading, tool registration, and agent execution wiring.
|
|
182
|
+
- Add contract tests for markdown schema validation and clearer error messages.
|
|
183
|
+
- Add concurrency and isolation tests around Registry behavior.
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 couchbelag
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Hellm
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
Install the gem and add to the application's Gemfile by executing:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bundle add hellm
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
gem install hellm
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
Create a project-local `.hellm` directory with agent and skill definitions.
|
|
20
|
+
|
|
21
|
+
Minimal structure (as used in `example/`):
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
.hellm/
|
|
25
|
+
agents/
|
|
26
|
+
alfred.agent.md
|
|
27
|
+
mathematician.agent.md
|
|
28
|
+
skills/
|
|
29
|
+
farben/
|
|
30
|
+
SKILL.md
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Set your OpenAI API key in an environment variable named `OPENAI_API_KEY` or put this variable in a `.env` file in the working directory:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
OPENAI_API_KEY=your_api_key_here
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Run an agent by passing the agent name and prompt:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
hellm Alfred "Tell me your favorite color and which month fits it."
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
### Builtin Tools
|
|
47
|
+
|
|
48
|
+
The framework provides the following tools for agents to use:
|
|
49
|
+
|
|
50
|
+
* `calculator` - a simple calculator for basic arithmetic operations.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
### Custom Tools
|
|
54
|
+
|
|
55
|
+
Custom tools can be defined using `Langchain::ToolDefinition`, like it is described in the [Langchain documentation](https://github.com/patterns-ai-core/langchainrb#creating-custom-tools). Such tools need to be registered in `Hellm` before they can be used by agents:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
Hellm::Tools::Registry.instance.add(:calculator) do
|
|
59
|
+
Langchain::Tool::Calculator.new
|
|
60
|
+
end
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The code shows how to get access to the registry and add a custom tool with a unique name. The block is used as factory method to create a new instance of the tool when needed. Instances are created once only and reused for all agents, so do not store any state in the tool instance.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
### Example
|
|
67
|
+
|
|
68
|
+
The example in this repository provides a convenience script which runs the example with one call, showcasing the flow of actions taken by the agents:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
cd example
|
|
72
|
+
./example.sh
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
## Development
|
|
77
|
+
|
|
78
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
79
|
+
|
|
80
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
81
|
+
|
|
82
|
+
## Contributing
|
|
83
|
+
|
|
84
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/hellm.
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rspec/core/rake_task"
|
|
5
|
+
|
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
7
|
+
|
|
8
|
+
task default: :spec
|
|
9
|
+
|
|
10
|
+
desc "returns current version number as defined in lib/hellm/version.rb"
|
|
11
|
+
task :version do
|
|
12
|
+
puts Hellm::VERSION
|
|
13
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.0.1
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Alfred
|
|
3
|
+
description: "General purpose agent."
|
|
4
|
+
tools: [agent]
|
|
5
|
+
agents: [Mathematician]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are Alfred, a helpful general purpose agent that ends every response's last sentence with ", Sir.". If you have a mathematical task, try to solve the problem by asking the Mathematician agent for help.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Mathematician
|
|
3
|
+
description: "Agent for calculating mathematical expressions."
|
|
4
|
+
argument-hint: "Provide a mathematical expression to calculate."
|
|
5
|
+
tools: [calculator]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are Mathematician, a helpful agent that calculates mathematical expressions accurately with the calculator tool.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: farben
|
|
3
|
+
description: "A skill for mapping colors to month"
|
|
4
|
+
argument-hint: "Describe your color and why you like it."
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Map colors to months:
|
|
8
|
+
|
|
9
|
+
* red: january
|
|
10
|
+
* orange: february
|
|
11
|
+
* yellow: march
|
|
12
|
+
* green: april
|
|
13
|
+
* blue: may
|
|
14
|
+
* purple: june
|
|
15
|
+
* pink: july
|
|
16
|
+
* white: august
|
|
17
|
+
* black: september
|
|
18
|
+
* brown: october
|
|
19
|
+
* gray: november
|
|
20
|
+
* rainbow: december
|
data/example/example.sh
ADDED
data/lib/hellm/agent.rb
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
require_relative "stream_adapter"
|
|
2
|
+
require_relative "tools/registry"
|
|
3
|
+
require "base64"
|
|
4
|
+
require "langchain"
|
|
5
|
+
|
|
6
|
+
class Hellm::Agent
|
|
7
|
+
attr_accessor :name, :llm, :description, :agents
|
|
8
|
+
|
|
9
|
+
# @param stream_adapter [Hellm::StreamAdapter] an object that responds to `message` and `tool_call` for streaming messages and tool calls
|
|
10
|
+
attr_accessor :stream_adapter
|
|
11
|
+
|
|
12
|
+
# instructions (string) for the agent.
|
|
13
|
+
attr_accessor :instructions
|
|
14
|
+
|
|
15
|
+
# array of tools (Langchain::Tool) for the agent to use
|
|
16
|
+
#
|
|
17
|
+
# langchain.rb Built-in Tools:
|
|
18
|
+
# - Langchain::Tool::Calculator: Useful for evaluating math expressions. Requires gem "eqn".
|
|
19
|
+
# - Langchain::Tool::Database: Connect your SQL database. Requires gem "sequel".
|
|
20
|
+
# - Langchain::Tool::FileSystem: Interact with the file system (read & write).
|
|
21
|
+
# - Langchain::Tool::GoogleSearch: Wrapper around SerpApi's Google Search API. Requires gem "google_search_results".
|
|
22
|
+
# - Langchain::Tool::NewsRetriever: A wrapper around NewsApi.org to fetch news articles.
|
|
23
|
+
# - Langchain::Tool::RubyCodeInterpreter: Useful for evaluating generated Ruby code. Requires gem "safe_ruby" (In need of a better solution).
|
|
24
|
+
# - Langchain::Tool::Tavily: A wrapper around Tavily AI.
|
|
25
|
+
# - Langchain::Tool::Vectorsearch: A wrapper for vector search classes.
|
|
26
|
+
# - Langchain::Tool::Weather: Calls Open Weather API to retrieve the current weather.
|
|
27
|
+
# - Langchain::Tool::Wikipedia: Calls Wikipedia API. Requires gem "wikipedia-client".
|
|
28
|
+
#
|
|
29
|
+
attr_accessor :tools
|
|
30
|
+
|
|
31
|
+
def initialize(model: nil, name: nil, description: nil, stream_adapter: nil, openai_api_key: nil, agents: nil, tools: nil, instructions: nil)
|
|
32
|
+
model ||= "gpt-5-nano"
|
|
33
|
+
Hellm.logger.debug("Initializing agent #{name} (#{model})")
|
|
34
|
+
@llm = Langchain::LLM::OpenAI.new(
|
|
35
|
+
api_key: openai_api_key || ::Hellm.openai_api_key,
|
|
36
|
+
default_options: {chat_model: model}
|
|
37
|
+
)
|
|
38
|
+
self.name = name || self.class.name
|
|
39
|
+
self.description = description || ""
|
|
40
|
+
self.instructions = instructions || ""
|
|
41
|
+
self.stream_adapter = stream_adapter || Hellm::StreamAdapter.new
|
|
42
|
+
self.agents = agents || []
|
|
43
|
+
self.tools = tools || []
|
|
44
|
+
# remove "agent" from tools, this ability is controlled by the presence of agents
|
|
45
|
+
self.tools.reject!{|t| t == "agent"}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def add(content: nil, image: nil)
|
|
50
|
+
return if content.nil? && image.nil?
|
|
51
|
+
options = {}
|
|
52
|
+
options[:content] = content if !content.nil? && !content.empty?
|
|
53
|
+
options[:image_url] = normalize_image_input(image) if !image.nil? && !image.empty?
|
|
54
|
+
assistant.add_message(**options)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def run
|
|
58
|
+
with_rate_limit do
|
|
59
|
+
assistant.run!
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def response
|
|
64
|
+
assistant_message = assistant.messages.reverse.find(&:llm?)
|
|
65
|
+
assistant_message&.content.to_s.strip
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def new_session
|
|
69
|
+
@assistant = nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
protected
|
|
73
|
+
def assistant
|
|
74
|
+
if @assistant.nil?
|
|
75
|
+
tool_instances = [
|
|
76
|
+
*Hellm::Tools::Registry.instance.get(*tools),
|
|
77
|
+
*Hellm::Tools::Registry.instance.get_agent_tools(*agents)
|
|
78
|
+
]
|
|
79
|
+
@assistant = Langchain::Assistant.new(
|
|
80
|
+
llm: llm,
|
|
81
|
+
tools: tool_instances,
|
|
82
|
+
instructions: instructions
|
|
83
|
+
)
|
|
84
|
+
@assistant.add_message_callback = ->(message) {
|
|
85
|
+
Hellm.logger.debug("[Agent #{name} message] #{message.inspect}")
|
|
86
|
+
self.stream_adapter.message(message)
|
|
87
|
+
}
|
|
88
|
+
@assistant.tool_execution_callback = ->(call_id, name, method, args) {
|
|
89
|
+
Hellm.logger.debug("[Agent #{self.name} tool_call] call_id: #{call_id}, name: #{name}, method: #{method}, args: #{args}")
|
|
90
|
+
self.stream_adapter.tool_call(call_id, name, method, args)
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
@assistant
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def build_instruction_preface(agent_tool)
|
|
97
|
+
preamble = "# Hints\n\n"
|
|
98
|
+
preamble = "#{preamble}You are an AI agent named #{name}. You have access to the following skills and sub-agents:\n\n"
|
|
99
|
+
preamble << agent_tool.instructions_for_agents(*agents)
|
|
100
|
+
preamble << "\n\n"
|
|
101
|
+
preamble
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def normalize_image_input(image_input)
|
|
105
|
+
return image_input if image_input.start_with?("http://", "https://", "data:image/")
|
|
106
|
+
|
|
107
|
+
path = File.expand_path(image_input)
|
|
108
|
+
raise ArgumentError, "Image file not found: #{image_input}" unless File.exist?(path)
|
|
109
|
+
|
|
110
|
+
ext = File.extname(path).downcase
|
|
111
|
+
mime_type = case ext
|
|
112
|
+
when ".jpg", ".jpeg" then "image/jpeg"
|
|
113
|
+
when ".png" then "image/png"
|
|
114
|
+
when ".webp" then "image/webp"
|
|
115
|
+
when ".gif" then "image/gif"
|
|
116
|
+
else "application/octet-stream"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
encoded = Base64.strict_encode64(File.binread(path))
|
|
120
|
+
"data:#{mime_type};base64,#{encoded}"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def rate_limited?(error)
|
|
124
|
+
message = error.message.downcase
|
|
125
|
+
message.include?("429") ||
|
|
126
|
+
message.include?("rate limit") ||
|
|
127
|
+
message.include?("rate_limit_exceeded")
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def with_rate_limit
|
|
131
|
+
attempts = 0
|
|
132
|
+
begin
|
|
133
|
+
attempts += 1
|
|
134
|
+
yield
|
|
135
|
+
rescue StandardError => e
|
|
136
|
+
raise unless rate_limited?(e) && attempts < MAX_RETRIES
|
|
137
|
+
# Small exponential backoff for transient 429 errors.
|
|
138
|
+
sleep(0.25 * (2**(attempts - 1)))
|
|
139
|
+
retry
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
require_relative "agent"
|
|
2
|
+
require_relative "skill"
|
|
3
|
+
require_relative "stream_adapter"
|
|
4
|
+
require_relative "tools/skill_tool"
|
|
5
|
+
require_relative "markdown_file"
|
|
6
|
+
|
|
7
|
+
class Hellm::Builder
|
|
8
|
+
attr_accessor :project_dir, :hellm_dir, :stream_adapter
|
|
9
|
+
|
|
10
|
+
SUPPORTED_SKILL_ATTRIBUTEES = ["name", "description", "argument_hint"]
|
|
11
|
+
SUPPORTED_AGENT_ATTRIBUTEES = ["model", "name", "description", "tools", "agents"]
|
|
12
|
+
|
|
13
|
+
def initialize(project_dir: nil, stream_adapter: nil, openai_api_key: nil)
|
|
14
|
+
@project_dir = project_dir
|
|
15
|
+
@hellm_dir = File.join(project_dir, ".hellm")
|
|
16
|
+
@hellm_dir = File.join(project_dir, ".github") if !Dir.exist?(@hellm_dir)
|
|
17
|
+
raise ArgumentError, "not found: #{project_dir}/.hellm" unless Dir.exist?(@hellm_dir)
|
|
18
|
+
|
|
19
|
+
@stream_adapter = stream_adapter
|
|
20
|
+
@openai_api_key = openai_api_key
|
|
21
|
+
|
|
22
|
+
add_builtin_tools_to_registry
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def build(talking_to: nil)
|
|
26
|
+
agents = get_agents_from_project_dir
|
|
27
|
+
skills = get_skills_from_project_dir
|
|
28
|
+
skill_tool = Hellm::Tools::SkillTool.new
|
|
29
|
+
skill_tool.add(*skills)
|
|
30
|
+
Hellm::Tools::Registry.instance.add(:skill, skill_tool)
|
|
31
|
+
Hellm::Tools::Registry.instance.add_agents(*agents)
|
|
32
|
+
|
|
33
|
+
if agents.empty?
|
|
34
|
+
agent = Hellm::Agent.new
|
|
35
|
+
else
|
|
36
|
+
agent = agents.find{ |agent| agent.name == talking_to }
|
|
37
|
+
raise ArgumentError, "Agent not found: #{talking_to}" if agent.nil?
|
|
38
|
+
end
|
|
39
|
+
agent
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
def add_builtin_tools_to_registry
|
|
44
|
+
Hellm::Tools::Registry.instance.add(:calculator) { Langchain::Tool::Calculator.new }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def get_skills_from_project_dir
|
|
48
|
+
skills_dir = File.join(hellm_dir, "skills")
|
|
49
|
+
return unless Dir.exist?(skills_dir)
|
|
50
|
+
|
|
51
|
+
Dir.glob(File.join(skills_dir, "**/SKILL.md")).map do |file|
|
|
52
|
+
markdown_dir = File.dirname(file)
|
|
53
|
+
markdown_file = Hellm::MarkdownFile.read(file)
|
|
54
|
+
Hellm::Skill.new(
|
|
55
|
+
dir: markdown_dir,
|
|
56
|
+
name: markdown_file.fetch("name"),
|
|
57
|
+
description: markdown_file.fetch("description"),
|
|
58
|
+
argument_hint: markdown_file.fetch("argument-hint"),
|
|
59
|
+
content: markdown_file.content,
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def get_agents_from_project_dir
|
|
65
|
+
agents_dir = File.join(hellm_dir, "agents")
|
|
66
|
+
return unless Dir.exist?(agents_dir)
|
|
67
|
+
|
|
68
|
+
Dir.glob(File.join(agents_dir, "*.md")).map do |file|
|
|
69
|
+
markdown_file = Hellm::MarkdownFile.read(file)
|
|
70
|
+
|
|
71
|
+
tools = markdown_file.fetch("tools", [])
|
|
72
|
+
tools << "skill"
|
|
73
|
+
|
|
74
|
+
Hellm::Agent.new(
|
|
75
|
+
model: markdown_file.fetch("model"),
|
|
76
|
+
name: markdown_file.fetch("name"),
|
|
77
|
+
description: markdown_file.fetch("description"),
|
|
78
|
+
tools: tools,
|
|
79
|
+
agents: markdown_file.fetch("agents", []),
|
|
80
|
+
instructions: markdown_file.content.strip,
|
|
81
|
+
stream_adapter: stream_adapter,
|
|
82
|
+
openai_api_key: @openai_api_key
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
|
|
3
|
+
class Hellm::MarkdownFile
|
|
4
|
+
attr_accessor :path, :attributes, :content
|
|
5
|
+
|
|
6
|
+
def self.read(path)
|
|
7
|
+
file = new(path)
|
|
8
|
+
file.read
|
|
9
|
+
file
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def fetch(key, default=nil)
|
|
13
|
+
attributes.fetch(key.to_s, default)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def read
|
|
17
|
+
file = File.read(path)
|
|
18
|
+
frontmatter, content = separate_frontmatter_and_content(file)
|
|
19
|
+
self.attributes = YAML.load(frontmatter) if frontmatter != ""
|
|
20
|
+
self.attributes ||= {}
|
|
21
|
+
self.content = content
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
protected
|
|
27
|
+
def initialize(path)
|
|
28
|
+
@path = path
|
|
29
|
+
@attributes = {}
|
|
30
|
+
@content = ""
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def separate_frontmatter_and_content(file)
|
|
34
|
+
within_frontmatter = false
|
|
35
|
+
content = ""
|
|
36
|
+
frontmatter = ""
|
|
37
|
+
|
|
38
|
+
file.lines.each do |line|
|
|
39
|
+
if line.strip == "---"
|
|
40
|
+
within_frontmatter = !within_frontmatter
|
|
41
|
+
next
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if within_frontmatter
|
|
45
|
+
frontmatter << line
|
|
46
|
+
else
|
|
47
|
+
content << line
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
[frontmatter, content]
|
|
52
|
+
end
|
|
53
|
+
end
|
data/lib/hellm/skill.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class Hellm::Skill
|
|
2
|
+
attr_accessor :dir, :name, :description, :argument_hint, :content
|
|
3
|
+
|
|
4
|
+
def initialize(dir:, name:, description:, argument_hint: nil, content: nil)
|
|
5
|
+
@dir = dir
|
|
6
|
+
@name = name
|
|
7
|
+
@description = description
|
|
8
|
+
@argument_hint = argument_hint
|
|
9
|
+
@content = content
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Hellm::Tools
|
|
2
|
+
require "langchain"
|
|
3
|
+
|
|
4
|
+
class AgentToolFactory
|
|
5
|
+
def self.create_sub_agent_tool(agent_name, agent)
|
|
6
|
+
klass = Class.new do
|
|
7
|
+
@@agent_name = agent_name
|
|
8
|
+
|
|
9
|
+
extend Langchain::ToolDefinition
|
|
10
|
+
|
|
11
|
+
def self.tool_name
|
|
12
|
+
@@agent_name
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
define_function "run_agent", description: "Run the agent #{agent.name} and return its response.\n#{agent.description}" do
|
|
16
|
+
property :content, type: "string", description: "Textual content to pass to the agent.", required: false
|
|
17
|
+
property :image, type: "string", description: "Image URL or path to pass to the agent.", required: false
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize(agent)
|
|
21
|
+
@agent = agent
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def run_agent(content: nil, image: nil)
|
|
25
|
+
@agent.new_session
|
|
26
|
+
@agent.add(content: content, image: image)
|
|
27
|
+
@agent.run
|
|
28
|
+
@agent.response
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
klass.new(agent)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require_relative "agent_tool_factory"
|
|
2
|
+
|
|
3
|
+
module Hellm::Tools
|
|
4
|
+
class Registry
|
|
5
|
+
def self.instance
|
|
6
|
+
@instance ||= new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@tools = {}
|
|
11
|
+
@agent_tools = {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def add(tool_name, instance=nil, &factory)
|
|
15
|
+
@tools[tool_name.to_s] = {
|
|
16
|
+
factory: factory,
|
|
17
|
+
instance: instance
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def get(*tool_names)
|
|
22
|
+
tools = tool_names.map do |tool_name|
|
|
23
|
+
entry = @tools[tool_name.to_s]
|
|
24
|
+
if entry.nil?
|
|
25
|
+
raise ArgumentError, "Tool not found: #{tool_name}"
|
|
26
|
+
end
|
|
27
|
+
entry[:instance] ||= entry[:factory].call
|
|
28
|
+
entry[:instance]
|
|
29
|
+
end
|
|
30
|
+
tools.compact
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def add_agents(*agents)
|
|
34
|
+
agents.each do |agent|
|
|
35
|
+
tool_name = agent_tool_name(agent.name)
|
|
36
|
+
tool = Hellm::Tools::AgentToolFactory.create_sub_agent_tool(tool_name, agent)
|
|
37
|
+
add(tool_name, tool)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def get_agent_tools(*agent_names)
|
|
42
|
+
tool_names = agent_names.map do |name|
|
|
43
|
+
agent_tool_name(name)
|
|
44
|
+
end
|
|
45
|
+
get(*tool_names)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
def agent_tool_name(agent_name)
|
|
50
|
+
"agent_tool_" + agent_name.gsub(/[\s-]+/, "_").downcase
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Hellm::Tools
|
|
2
|
+
require "langchain"
|
|
3
|
+
|
|
4
|
+
class SkillTool
|
|
5
|
+
extend Langchain::ToolDefinition
|
|
6
|
+
|
|
7
|
+
def add(*skills)
|
|
8
|
+
skills.each do |skill|
|
|
9
|
+
add_skill(skill)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
def add_skill(skill)
|
|
15
|
+
skill_name = skill.name.gsub(/[\s-]+/, "_").downcase
|
|
16
|
+
method_name = "get_skill_#{skill_name}"
|
|
17
|
+
self.class.class_eval(
|
|
18
|
+
<<~EOSKILL
|
|
19
|
+
define_function "#{method_name}", description: "SkillTool: Read skill #{skill.name}.\n#{skill.description}\n#{skill.argument_hint}\n\n"
|
|
20
|
+
def #{method_name}
|
|
21
|
+
<<~EOCONTENT
|
|
22
|
+
#{skill.content}
|
|
23
|
+
EOCONTENT
|
|
24
|
+
end
|
|
25
|
+
EOSKILL
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/hellm.rb
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "hellm/version"
|
|
4
|
+
require_relative "hellm/builder"
|
|
5
|
+
|
|
6
|
+
module Hellm
|
|
7
|
+
class Error < StandardError; end
|
|
8
|
+
# Your code goes here...
|
|
9
|
+
|
|
10
|
+
def self.openai_api_key=(key)
|
|
11
|
+
@api_key = key if !key.nil? && !key.empty?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.openai_api_key
|
|
15
|
+
@api_key ||= ENV["OPENAI_API_KEY"]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.build_agent(agent_name, project_dir, stream_adapter: nil, openai_api_key: nil)
|
|
19
|
+
self.openai_api_key = openai_api_key
|
|
20
|
+
builder = Hellm::Builder.new(project_dir: project_dir, stream_adapter: stream_adapter, openai_api_key: openai_api_key)
|
|
21
|
+
builder.build(talking_to: agent_name)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.logger=(logger)
|
|
25
|
+
@logger = logger
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.logger
|
|
29
|
+
@logger ||= Logger.new($stdout)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.set_log_level(level, langchain_level: Logger::ERROR)
|
|
33
|
+
self.logger.level = level
|
|
34
|
+
Langchain.logger.level = langchain_level
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# default log level
|
|
39
|
+
Hellm.set_log_level(Logger::DEBUG)
|
data/sig/hellm.rbs
ADDED
metadata
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: hellm
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- ubity UG
|
|
8
|
+
- Tobi Schmid
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: langchainrb
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 0.19.5
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 0.19.5
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: ruby-openai
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 8.3.0
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 8.3.0
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: base64
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 0.3.0
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 0.3.0
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: eqn
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 1.6.5
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: 1.6.5
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: google_search_results
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: 2.2.0
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: 2.2.0
|
|
83
|
+
description: |
|
|
84
|
+
Agentic framework for LLMs, allowing you to build autonomous agents that can perform complex tasks and interact with the world.
|
|
85
|
+
Name is inspired by the English word "helm", which refers to the steering mechanism of a ship, symbolizing guidance and direction.
|
|
86
|
+
The name also evokes the idea of taking control and navigating through challenges, much like how an agentic framework empowers LLMs to make decisions and take actions autonomously.
|
|
87
|
+
email:
|
|
88
|
+
- info@ubity.io
|
|
89
|
+
executables: []
|
|
90
|
+
extensions: []
|
|
91
|
+
extra_rdoc_files: []
|
|
92
|
+
files:
|
|
93
|
+
- ".github/agents/developer.md"
|
|
94
|
+
- ".github/skills/ruby/SKILL.md"
|
|
95
|
+
- ".github/skills/test/SKILL.md"
|
|
96
|
+
- ARCHITECTURE.md
|
|
97
|
+
- LICENSE.txt
|
|
98
|
+
- README.md
|
|
99
|
+
- Rakefile
|
|
100
|
+
- VERSION
|
|
101
|
+
- example/.hellm/agents/alfred.agent.md
|
|
102
|
+
- example/.hellm/agents/mathematician.agent.md
|
|
103
|
+
- example/.hellm/skills/farben/SKILL.md
|
|
104
|
+
- example/example.sh
|
|
105
|
+
- lib/hellm.rb
|
|
106
|
+
- lib/hellm/agent.rb
|
|
107
|
+
- lib/hellm/builder.rb
|
|
108
|
+
- lib/hellm/markdown_file.rb
|
|
109
|
+
- lib/hellm/skill.rb
|
|
110
|
+
- lib/hellm/stream_adapter.rb
|
|
111
|
+
- lib/hellm/tools/agent_tool_factory.rb
|
|
112
|
+
- lib/hellm/tools/registry.rb
|
|
113
|
+
- lib/hellm/tools/skill_tool.rb
|
|
114
|
+
- lib/hellm/version.rb
|
|
115
|
+
- sig/hellm.rbs
|
|
116
|
+
homepage: https://gitlab.com/ubity/gems/hellm
|
|
117
|
+
licenses:
|
|
118
|
+
- MIT
|
|
119
|
+
metadata:
|
|
120
|
+
homepage_uri: https://gitlab.com/ubity/gems/hellm
|
|
121
|
+
source_code_uri: https://gitlab.com/ubity/gems/hellm
|
|
122
|
+
rdoc_options: []
|
|
123
|
+
require_paths:
|
|
124
|
+
- lib
|
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
|
+
requirements:
|
|
127
|
+
- - ">="
|
|
128
|
+
- !ruby/object:Gem::Version
|
|
129
|
+
version: 3.4.1
|
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
|
+
requirements:
|
|
132
|
+
- - ">="
|
|
133
|
+
- !ruby/object:Gem::Version
|
|
134
|
+
version: '0'
|
|
135
|
+
requirements: []
|
|
136
|
+
rubygems_version: 3.6.9
|
|
137
|
+
specification_version: 4
|
|
138
|
+
summary: Ruby agentic framework for LLMs
|
|
139
|
+
test_files: []
|