active_harness 0.3.8 → 0.3.9
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/lib/active_harness/agent/goal_check.rb +26 -0
- data/lib/active_harness/agent/state.rb +90 -0
- data/lib/active_harness/agent.rb +734 -0
- data/lib/active_harness/core/errors.rb +11 -0
- data/lib/active_harness/pipeline/hooks.rb +1 -1
- data/lib/active_harness/providers/anthropic.rb +57 -8
- data/lib/active_harness/providers/base.rb +6 -0
- data/lib/active_harness/providers/deepseek.rb +7 -4
- data/lib/active_harness/providers/gpustack.rb +7 -4
- data/lib/active_harness/providers/groq.rb +7 -4
- data/lib/active_harness/providers/hetzner.rb +7 -4
- data/lib/active_harness/providers/mistral.rb +7 -4
- data/lib/active_harness/providers/ollama.rb +7 -4
- data/lib/active_harness/providers/ollama_cloud.rb +7 -4
- data/lib/active_harness/providers/openai.rb +7 -4
- data/lib/active_harness/providers/openrouter.rb +7 -4
- data/lib/active_harness/providers/tool_calling.rb +95 -0
- data/lib/active_harness/providers/xai.rb +7 -4
- data/lib/active_harness/railtie.rb +1 -1
- data/lib/active_harness/request/hooks.rb +1 -1
- data/lib/active_harness/request/providers.rb +15 -0
- data/lib/active_harness/request.rb +39 -7
- data/lib/active_harness/result.rb +4 -0
- data/lib/active_harness/tool/path_policy.rb +28 -0
- data/lib/active_harness/tool.rb +348 -0
- data/lib/active_harness/tribunal/hooks.rb +1 -1
- data/lib/active_harness.rb +4 -1
- data/lib/generators/active_harness/agent/agent_generator.rb +16 -0
- data/lib/generators/active_harness/agent/templates/agent.rb.tt +14 -0
- data/lib/generators/active_harness/goal/goal_generator.rb +16 -0
- data/lib/generators/active_harness/goal/templates/goal.rb.tt +14 -0
- data/lib/generators/active_harness/install/install_generator.rb +30 -25
- data/lib/generators/active_harness/install/templates/agents/files_agent.rb +39 -0
- data/lib/generators/active_harness/install/templates/concerns/agent_tracing.rb +36 -0
- data/lib/generators/active_harness/install/templates/concerns/pipeline_tracing.rb +28 -0
- data/lib/generators/active_harness/install/templates/concerns/request_tracing.rb +44 -0
- data/lib/generators/active_harness/install/templates/concerns/tool_tracing.rb +21 -0
- data/lib/generators/active_harness/install/templates/concerns/tracing_log.rb +18 -0
- data/lib/generators/active_harness/install/templates/concerns/tribunal_tracing.rb +28 -0
- data/lib/generators/active_harness/install/templates/controllers/ai_controller.rb +23 -2
- data/lib/generators/active_harness/install/templates/goals/answer_present_goal.rb +22 -0
- data/lib/generators/active_harness/install/templates/pipelines/support_pipeline.rb +8 -18
- data/lib/generators/active_harness/install/templates/prompts/files_prompt.rb +9 -0
- data/lib/generators/active_harness/install/templates/prompts/support_prompt.rb +3 -0
- data/lib/generators/active_harness/install/templates/requests/files_request.rb +18 -0
- data/lib/generators/active_harness/install/templates/requests/support_guard_request.rb +12 -0
- data/lib/generators/active_harness/install/templates/requests/support_request.rb +6 -0
- data/lib/generators/active_harness/install/templates/tools/ls_tool.rb +40 -0
- data/lib/generators/active_harness/install/templates/tools/read_file_tool.rb +43 -0
- data/lib/generators/active_harness/install/templates/tribunals/support_guard_tribunal.rb +7 -0
- data/lib/generators/active_harness/pipeline/templates/pipeline.rb.tt +5 -0
- data/lib/generators/active_harness/request/templates/request.rb.tt +2 -0
- data/lib/generators/active_harness/tool/templates/tool.rb.tt +22 -0
- data/lib/generators/active_harness/tool/tool_generator.rb +16 -0
- data/lib/generators/active_harness/tribunal/templates/tribunal.rb.tt +3 -0
- metadata +31 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5549eddee812779586e86bbe67c2e72d077b7770b98421b0a43dbcb6dca7ab00
|
|
4
|
+
data.tar.gz: 9249bc587c75dfe7f0d5141b311f46ba0dd4b90b308873b31535044bd7f37ef2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6aebf953bbb38bb9c7f3ea943b2b1f10efefaa7371ba34b8aad0e8f921a42749731af2473723fc870a4581f84cbf364315435ce18d7f5f78809c175de86607dd
|
|
7
|
+
data.tar.gz: ee32ce687ef93c1616b659ed39ca49e0ac9c98705a7f2768e00bf5ba207cdae92938eaad8012cd8b9bad6413b489419cc7f3c2e3dc6138d8ad5af91dd8bc5ec1
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module ActiveHarness
|
|
2
|
+
# Base class for goal checks — the judge of an Agent's final answer.
|
|
3
|
+
# Subclass it and override #call:
|
|
4
|
+
#
|
|
5
|
+
# class TestsPassedGoal < ActiveHarness::GoalCheck
|
|
6
|
+
# def call(candidate:, goal:, state:)
|
|
7
|
+
# ok = system("bin/rails test")
|
|
8
|
+
# ActiveHarness::GoalCheckResult.new(status: ok ? :achieved : :continue, feedback: "Tests are failing")
|
|
9
|
+
# end
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# class DevelopmentAgent < ActiveHarness::Agent
|
|
13
|
+
# goal TestsPassedGoal
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# The base class exists to fail loudly: forgetting to define #call raises an error
|
|
17
|
+
# that names the class, instead of an obscure NoMethodError mid-run. It is optional —
|
|
18
|
+
# any object (or a block) with call(candidate:, goal:, state:) is accepted by `goal`.
|
|
19
|
+
class GoalCheck
|
|
20
|
+
def call(candidate:, goal:, state:)
|
|
21
|
+
raise NotImplementedError,
|
|
22
|
+
"#{self.class.name || 'GoalCheck'} must define #call(candidate:, goal:, state:) " \
|
|
23
|
+
"returning an ActiveHarness::GoalCheckResult"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module ActiveHarness
|
|
2
|
+
# Intent + verification criteria of one Agent run. Built by +setup+ (a Pipeline);
|
|
3
|
+
# without +setup+ the runtime makes a minimal Goal from the input.
|
|
4
|
+
Goal = Struct.new(:task, :constraints, :success_criteria, :plan, keyword_init: true) do
|
|
5
|
+
def self.from(value)
|
|
6
|
+
case value
|
|
7
|
+
when Goal then value
|
|
8
|
+
when String then value.strip.empty? ? nil : new(task: value)
|
|
9
|
+
when Hash
|
|
10
|
+
h = value.each_with_object({}) { |(k, v), acc| acc[k.to_sym] = v }
|
|
11
|
+
return nil if h[:task].to_s.strip.empty?
|
|
12
|
+
|
|
13
|
+
new(task: h[:task], constraints: h[:constraints], success_criteria: h[:success_criteria], plan: h[:plan])
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# What a goal check returns.
|
|
19
|
+
#
|
|
20
|
+
# status — :achieved | :continue | :replan | :needs_input | :failed
|
|
21
|
+
# feedback — text for the model (for :continue / :replan)
|
|
22
|
+
# evidence — anything useful for the trace
|
|
23
|
+
GoalCheckResult = Struct.new(:status, :feedback, :evidence, keyword_init: true) do
|
|
24
|
+
def self.from(value)
|
|
25
|
+
case value
|
|
26
|
+
when GoalCheckResult then value
|
|
27
|
+
when true then new(status: :achieved)
|
|
28
|
+
when false, nil then new(status: :continue, feedback: "The goal is not reached yet.")
|
|
29
|
+
when Hash
|
|
30
|
+
new(status: (value[:status] || value["status"])&.to_sym, feedback: value[:feedback] || value["feedback"], evidence: value[:evidence] || value["evidence"])
|
|
31
|
+
else
|
|
32
|
+
raise ArgumentError, "A goal check must return GoalCheckResult (or true/false/Hash), got #{value.class}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# One tool call requested by the model, with its outcome.
|
|
38
|
+
#
|
|
39
|
+
# id — provider call id; the tool message is bound to it
|
|
40
|
+
# name — wire name (os__ls)
|
|
41
|
+
# tool — Tool class (nil for an unknown name)
|
|
42
|
+
# input — validated arguments (symbol keys) or the raw value if invalid
|
|
43
|
+
# result — ToolResult
|
|
44
|
+
ToolCall = Struct.new(:id, :name, :tool, :input, :result, keyword_init: true) do
|
|
45
|
+
def full_name
|
|
46
|
+
tool ? tool.full_name : name
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def denied?
|
|
50
|
+
!result.nil? && result.status == :denied
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def error?
|
|
54
|
+
!result.nil? && result.status == :error
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# One iteration of the Agent loop.
|
|
59
|
+
AgentTurn = Struct.new(:number, :request_name, :request, :response, :tool_calls, :steps, :verdict, :ms, keyword_init: true) do
|
|
60
|
+
def denied_calls
|
|
61
|
+
tool_calls.select(&:denied?)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def error_calls
|
|
65
|
+
tool_calls.select(&:error?)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Trustworthy data of one run — the source of truth. The model-facing context is
|
|
70
|
+
# only a view built from it by the context builder.
|
|
71
|
+
#
|
|
72
|
+
# messages — normalized conversation (user / assistant / tool)
|
|
73
|
+
# events — full journal of turns, denials, tool calls and results; never summarized
|
|
74
|
+
AgentState = Struct.new(
|
|
75
|
+
:run_id, :status, :input, :goal, :plan,
|
|
76
|
+
:messages, :events, :artifacts,
|
|
77
|
+
:turn, :tool_calls_count, :usage, :stop_reason,
|
|
78
|
+
keyword_init: true
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
class Agent
|
|
82
|
+
# Default context builder: the full history. A custom builder is any object with
|
|
83
|
+
# call(state:) returning a messages array (a shortened view, never a rewrite of the state).
|
|
84
|
+
class ContextBuilder
|
|
85
|
+
def call(state:)
|
|
86
|
+
state.messages.map(&:dup)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|