ai_sentinel 0.2.2 → 0.3.0
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 +4 -4
- data/README.md +2 -0
- data/exe/ai_sentinel +7 -1
- data/lib/ai_sentinel/providers/anthropic.rb +2 -2
- data/lib/ai_sentinel/providers/base.rb +1 -1
- data/lib/ai_sentinel/providers/openai.rb +7 -9
- data/lib/ai_sentinel/runner.rb +1 -1
- data/lib/ai_sentinel/scheduler.rb +15 -2
- data/lib/ai_sentinel/version.rb +1 -1
- data/lib/ai_sentinel.rb +8 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 57b7b717300a40c7538e4a7da84bd574677c9bd38fa55c094ad44725354e07d8
|
|
4
|
+
data.tar.gz: 1897eb521d5a9b2c71a1b1d54e7aa015172466accb65ef6c0bfa6a3b9ac5879a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 46a5467ba869e34a6b2e4f942c23cdcb719a8b2d8d6583ba29237873bb0145bed9ef736d6949bdf0434bf0f05fce8529b790b89f691594f40545667be12b574d
|
|
7
|
+
data.tar.gz: 8d05fe5f7477260054a4d9083fa2cbdcecb0effc5c2c701b1768fae3882604e9a8840e0cb5ceba0fb11b61a2fc5a488f1e3eef61d298f3d1e06c6d6e60327e64
|
data/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# AiSentinel
|
|
2
2
|
|
|
3
|
+
[](https://badge.fury.io/rb/ai_sentinel)
|
|
4
|
+
|
|
3
5
|
A lightweight Ruby gem for scheduling AI-driven tasks. Define workflows in a YAML config file that run on a cron schedule, process data through LLMs, and take conditional actions based on the results. Designed to be self-hostable on minimal hardware -- just Ruby and SQLite.
|
|
4
6
|
|
|
5
7
|
## Table of contents
|
data/exe/ai_sentinel
CHANGED
|
@@ -75,8 +75,8 @@ module AiSentinel
|
|
|
75
75
|
result = tool_executor.execute(tool_name, tool_input)
|
|
76
76
|
AiSentinel.logger.info(" Tool result: #{result.to_s[0..200]}")
|
|
77
77
|
{ 'type' => 'tool_result', 'tool_use_id' => tool_id, 'content' => result.to_s }
|
|
78
|
-
rescue
|
|
79
|
-
AiSentinel.
|
|
78
|
+
rescue StandardError => e
|
|
79
|
+
AiSentinel.log_error(e, context: "Tool '#{tool_name}' error")
|
|
80
80
|
{ 'type' => 'tool_result', 'tool_use_id' => tool_id, 'content' => "Error: #{e.message}",
|
|
81
81
|
'is_error' => true }
|
|
82
82
|
end
|
|
@@ -94,7 +94,7 @@ module AiSentinel
|
|
|
94
94
|
def compact_context(context_key)
|
|
95
95
|
ContextCompactor.new(context_key: context_key, configuration: configuration).compact_if_needed
|
|
96
96
|
rescue StandardError => e
|
|
97
|
-
AiSentinel.
|
|
97
|
+
AiSentinel.log_error(e, context: "Context compaction failed for '#{context_key}'")
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
def prune_old_messages(context_id)
|
|
@@ -61,19 +61,17 @@ module AiSentinel
|
|
|
61
61
|
def execute_tool_call(tool_executor, tool_call, round)
|
|
62
62
|
function = tool_call['function']
|
|
63
63
|
tool_name = function['name']
|
|
64
|
-
tool_input = JSON.parse(function['arguments'])
|
|
65
64
|
tool_id = tool_call['id']
|
|
66
65
|
|
|
67
66
|
AiSentinel.logger.info(" Tool call [round #{round + 1}]: #{tool_name}(#{function['arguments']})")
|
|
68
67
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
end
|
|
68
|
+
tool_input = JSON.parse(function['arguments'])
|
|
69
|
+
result = tool_executor.execute(tool_name, tool_input)
|
|
70
|
+
AiSentinel.logger.info(" Tool result: #{result.to_s[0..200]}")
|
|
71
|
+
{ 'role' => 'tool', 'tool_call_id' => tool_id, 'content' => result.to_s }
|
|
72
|
+
rescue StandardError => e
|
|
73
|
+
AiSentinel.log_error(e, context: "Tool '#{tool_name}' error")
|
|
74
|
+
{ 'role' => 'tool', 'tool_call_id' => tool_id, 'content' => "Error: #{e.message}" }
|
|
77
75
|
end
|
|
78
76
|
|
|
79
77
|
def build_messages(prompt, system, context_key, remember, limit: nil)
|
data/lib/ai_sentinel/runner.rb
CHANGED
|
@@ -31,7 +31,7 @@ module AiSentinel
|
|
|
31
31
|
context
|
|
32
32
|
rescue StandardError => e
|
|
33
33
|
Persistence::ExecutionLog.fail(execution_id, e.message)
|
|
34
|
-
AiSentinel.
|
|
34
|
+
AiSentinel.log_error(e, context: "Workflow '#{workflow.name}' failed")
|
|
35
35
|
raise
|
|
36
36
|
end
|
|
37
37
|
|
|
@@ -17,8 +17,11 @@ module AiSentinel
|
|
|
17
17
|
apply_working_directory
|
|
18
18
|
|
|
19
19
|
if daemonize
|
|
20
|
+
Persistence::Database.disconnect
|
|
20
21
|
Process.daemon(true, true)
|
|
22
|
+
Persistence::Database.setup(configuration.database_path)
|
|
21
23
|
write_pid_file
|
|
24
|
+
setup_crash_cleanup
|
|
22
25
|
AiSentinel.logger.info("AiSentinel started in background (PID #{Process.pid}, #{registry.size} workflow(s))")
|
|
23
26
|
else
|
|
24
27
|
AiSentinel.logger.info("AiSentinel started (#{registry.size} workflow(s)). Press Ctrl+C to stop.")
|
|
@@ -32,10 +35,16 @@ module AiSentinel
|
|
|
32
35
|
@rufus.join
|
|
33
36
|
cleanup_pid_file
|
|
34
37
|
AiSentinel.logger.info('AiSentinel stopped')
|
|
38
|
+
rescue StandardError => e
|
|
39
|
+
AiSentinel.log_error(e, context: 'Scheduler crashed')
|
|
40
|
+
cleanup_pid_file
|
|
41
|
+
raise
|
|
35
42
|
end
|
|
36
43
|
|
|
37
44
|
def stop
|
|
38
|
-
@rufus
|
|
45
|
+
@rufus&.shutdown
|
|
46
|
+
rescue StandardError => e
|
|
47
|
+
AiSentinel.log_error(e, context: 'Error during shutdown')
|
|
39
48
|
end
|
|
40
49
|
|
|
41
50
|
def trigger(workflow_name)
|
|
@@ -72,13 +81,17 @@ module AiSentinel
|
|
|
72
81
|
FileUtils.rm_f(pid_file)
|
|
73
82
|
end
|
|
74
83
|
|
|
84
|
+
def setup_crash_cleanup
|
|
85
|
+
at_exit { cleanup_pid_file }
|
|
86
|
+
end
|
|
87
|
+
|
|
75
88
|
def register_workflows
|
|
76
89
|
registry.each do |name, workflow|
|
|
77
90
|
@rufus.cron(workflow.schedule_expression) do
|
|
78
91
|
runner = Runner.new(workflow: workflow, configuration: configuration)
|
|
79
92
|
runner.execute
|
|
80
93
|
rescue StandardError => e
|
|
81
|
-
AiSentinel.
|
|
94
|
+
AiSentinel.log_error(e, context: "Workflow '#{name}' failed")
|
|
82
95
|
end
|
|
83
96
|
|
|
84
97
|
AiSentinel.logger.info("Registered workflow '#{name}' with schedule '#{workflow.schedule_expression}'")
|
data/lib/ai_sentinel/version.rb
CHANGED
data/lib/ai_sentinel.rb
CHANGED
|
@@ -50,6 +50,14 @@ module AiSentinel
|
|
|
50
50
|
configuration.logger
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
def log_error(error, context: nil)
|
|
54
|
+
message = context ? "#{context}: #{error.message}" : error.message
|
|
55
|
+
logger.error(message)
|
|
56
|
+
error.backtrace&.first(10)&.each { |line| logger.error(" #{line}") }
|
|
57
|
+
rescue StandardError
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
60
|
+
|
|
53
61
|
def resolve_api_key
|
|
54
62
|
configuration.api_key ||= ENV.fetch(configuration.env_key_name, nil)
|
|
55
63
|
end
|