rubyllm-observ 0.5.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 +7 -0
- data/README.md +778 -0
- data/Rakefile +49 -0
- data/app/assets/javascripts/observ/application.js +12 -0
- data/app/assets/javascripts/observ/controllers/autoscroll_controller.js +33 -0
- data/app/assets/javascripts/observ/controllers/chat_form_controller.js +93 -0
- data/app/assets/javascripts/observ/controllers/copy_controller.js +43 -0
- data/app/assets/javascripts/observ/controllers/dashboard_controller.js +58 -0
- data/app/assets/javascripts/observ/controllers/drawer_controller.js +58 -0
- data/app/assets/javascripts/observ/controllers/expandable_controller.js +33 -0
- data/app/assets/javascripts/observ/controllers/filter_controller.js +36 -0
- data/app/assets/javascripts/observ/controllers/index.js +52 -0
- data/app/assets/javascripts/observ/controllers/json_viewer_controller.js +260 -0
- data/app/assets/javascripts/observ/controllers/message_form_controller.js +58 -0
- data/app/assets/javascripts/observ/controllers/prompt_variables_controller.js +64 -0
- data/app/assets/javascripts/observ/controllers/text_select_controller.js +14 -0
- data/app/assets/stylesheets/observ/_annotations.scss +127 -0
- data/app/assets/stylesheets/observ/_card.scss +52 -0
- data/app/assets/stylesheets/observ/_chat.scss +156 -0
- data/app/assets/stylesheets/observ/_components.scss +460 -0
- data/app/assets/stylesheets/observ/_dashboard.scss +40 -0
- data/app/assets/stylesheets/observ/_datasets.scss +697 -0
- data/app/assets/stylesheets/observ/_drawer.scss +273 -0
- data/app/assets/stylesheets/observ/_json_viewer.scss +120 -0
- data/app/assets/stylesheets/observ/_layout.scss +256 -0
- data/app/assets/stylesheets/observ/_metrics.scss +99 -0
- data/app/assets/stylesheets/observ/_observations.scss +160 -0
- data/app/assets/stylesheets/observ/_pagination.scss +143 -0
- data/app/assets/stylesheets/observ/_prompts.scss +365 -0
- data/app/assets/stylesheets/observ/_table.scss +53 -0
- data/app/assets/stylesheets/observ/_variables.scss +53 -0
- data/app/assets/stylesheets/observ/application.scss +15 -0
- data/app/controllers/observ/annotations_controller.rb +144 -0
- data/app/controllers/observ/application_controller.rb +8 -0
- data/app/controllers/observ/chats_controller.rb +58 -0
- data/app/controllers/observ/dashboard_controller.rb +159 -0
- data/app/controllers/observ/dataset_items_controller.rb +85 -0
- data/app/controllers/observ/dataset_run_items_controller.rb +84 -0
- data/app/controllers/observ/dataset_runs_controller.rb +110 -0
- data/app/controllers/observ/datasets_controller.rb +74 -0
- data/app/controllers/observ/messages_controller.rb +26 -0
- data/app/controllers/observ/observations_controller.rb +59 -0
- data/app/controllers/observ/prompt_versions_controller.rb +148 -0
- data/app/controllers/observ/prompts_controller.rb +205 -0
- data/app/controllers/observ/sessions_controller.rb +45 -0
- data/app/controllers/observ/traces_controller.rb +86 -0
- data/app/forms/observ/prompt_form.rb +96 -0
- data/app/helpers/observ/application_helper.rb +9 -0
- data/app/helpers/observ/chats_helper.rb +47 -0
- data/app/helpers/observ/dashboard_helper.rb +154 -0
- data/app/helpers/observ/datasets_helper.rb +62 -0
- data/app/helpers/observ/pagination_helper.rb +38 -0
- data/app/jobs/observ/application_job.rb +4 -0
- data/app/jobs/observ/dataset_runner_job.rb +49 -0
- data/app/mailers/observ/application_mailer.rb +6 -0
- data/app/models/concerns/observ/agent_phaseable.rb +124 -0
- data/app/models/concerns/observ/agent_selectable.rb +50 -0
- data/app/models/concerns/observ/chat_enhancements.rb +109 -0
- data/app/models/concerns/observ/message_enhancements.rb +31 -0
- data/app/models/concerns/observ/observability_instrumentation.rb +124 -0
- data/app/models/concerns/observ/prompt_management.rb +320 -0
- data/app/models/concerns/observ/trace_association.rb +9 -0
- data/app/models/observ/annotation.rb +23 -0
- data/app/models/observ/application_record.rb +5 -0
- data/app/models/observ/dataset.rb +51 -0
- data/app/models/observ/dataset_item.rb +41 -0
- data/app/models/observ/dataset_run.rb +104 -0
- data/app/models/observ/dataset_run_item.rb +111 -0
- data/app/models/observ/generation.rb +56 -0
- data/app/models/observ/null_prompt.rb +59 -0
- data/app/models/observ/observation.rb +38 -0
- data/app/models/observ/prompt.rb +315 -0
- data/app/models/observ/score.rb +51 -0
- data/app/models/observ/session.rb +131 -0
- data/app/models/observ/span.rb +13 -0
- data/app/models/observ/trace.rb +135 -0
- data/app/presenters/observ/agent_select_presenter.rb +59 -0
- data/app/services/observ/agent_executor_service.rb +174 -0
- data/app/services/observ/agent_provider.rb +60 -0
- data/app/services/observ/agent_selection_service.rb +53 -0
- data/app/services/observ/chat_instrumenter.rb +523 -0
- data/app/services/observ/dataset_runner_service.rb +153 -0
- data/app/services/observ/evaluator_runner_service.rb +58 -0
- data/app/services/observ/evaluators/base_evaluator.rb +51 -0
- data/app/services/observ/evaluators/contains_evaluator.rb +53 -0
- data/app/services/observ/evaluators/exact_match_evaluator.rb +23 -0
- data/app/services/observ/evaluators/json_structure_evaluator.rb +44 -0
- data/app/services/observ/prompt_manager/cache_statistics.rb +82 -0
- data/app/services/observ/prompt_manager/caching.rb +167 -0
- data/app/services/observ/prompt_manager/comparison.rb +49 -0
- data/app/services/observ/prompt_manager/version_management.rb +96 -0
- data/app/services/observ/prompt_manager.rb +40 -0
- data/app/services/observ/trace_text_formatter.rb +349 -0
- data/app/validators/observ/prompt_config_validator.rb +187 -0
- data/app/views/kaminari/_first_page.html.erb +11 -0
- data/app/views/kaminari/_gap.html.erb +8 -0
- data/app/views/kaminari/_last_page.html.erb +11 -0
- data/app/views/kaminari/_next_page.html.erb +11 -0
- data/app/views/kaminari/_page.html.erb +12 -0
- data/app/views/kaminari/_paginator.html.erb +25 -0
- data/app/views/kaminari/_prev_page.html.erb +11 -0
- data/app/views/kaminari/observ/_first_page.html.erb +11 -0
- data/app/views/kaminari/observ/_gap.html.erb +8 -0
- data/app/views/kaminari/observ/_last_page.html.erb +11 -0
- data/app/views/kaminari/observ/_next_page.html.erb +11 -0
- data/app/views/kaminari/observ/_page.html.erb +12 -0
- data/app/views/kaminari/observ/_paginator.html.erb +25 -0
- data/app/views/kaminari/observ/_prev_page.html.erb +11 -0
- data/app/views/layouts/observ/application.html.erb +88 -0
- data/app/views/observ/annotations/_annotation.html.erb +13 -0
- data/app/views/observ/annotations/_form.html.erb +28 -0
- data/app/views/observ/annotations/index.html.erb +28 -0
- data/app/views/observ/annotations/sessions_index.html.erb +48 -0
- data/app/views/observ/annotations/traces_index.html.erb +48 -0
- data/app/views/observ/chats/_form.html.erb +45 -0
- data/app/views/observ/chats/index.html.erb +67 -0
- data/app/views/observ/chats/new.html.erb +17 -0
- data/app/views/observ/chats/show.html.erb +34 -0
- data/app/views/observ/dashboard/index.html.erb +236 -0
- data/app/views/observ/dataset_items/_form.html.erb +49 -0
- data/app/views/observ/dataset_items/edit.html.erb +18 -0
- data/app/views/observ/dataset_items/index.html.erb +95 -0
- data/app/views/observ/dataset_items/new.html.erb +18 -0
- data/app/views/observ/dataset_run_items/_score_close_drawer.html.erb +4 -0
- data/app/views/observ/dataset_run_items/_score_drawer.html.erb +75 -0
- data/app/views/observ/dataset_run_items/_score_success.html.erb +29 -0
- data/app/views/observ/dataset_run_items/_scores_cell.html.erb +19 -0
- data/app/views/observ/dataset_run_items/details_drawer.turbo_stream.erb +80 -0
- data/app/views/observ/dataset_run_items/score_drawer.turbo_stream.erb +7 -0
- data/app/views/observ/dataset_runs/index.html.erb +108 -0
- data/app/views/observ/dataset_runs/new.html.erb +57 -0
- data/app/views/observ/dataset_runs/review.html.erb +155 -0
- data/app/views/observ/dataset_runs/show.html.erb +166 -0
- data/app/views/observ/datasets/_form.html.erb +62 -0
- data/app/views/observ/datasets/_items_tab.html.erb +66 -0
- data/app/views/observ/datasets/_runs_tab.html.erb +82 -0
- data/app/views/observ/datasets/edit.html.erb +32 -0
- data/app/views/observ/datasets/index.html.erb +105 -0
- data/app/views/observ/datasets/new.html.erb +18 -0
- data/app/views/observ/datasets/show.html.erb +67 -0
- data/app/views/observ/messages/_content.html.erb +1 -0
- data/app/views/observ/messages/_form.html.erb +33 -0
- data/app/views/observ/messages/_message.html.erb +14 -0
- data/app/views/observ/messages/_tool_calls.html.erb +10 -0
- data/app/views/observ/messages/create.turbo_stream.erb +9 -0
- data/app/views/observ/observations/index.html.erb +97 -0
- data/app/views/observ/observations/show_generation.html.erb +195 -0
- data/app/views/observ/observations/show_span.html.erb +93 -0
- data/app/views/observ/prompts/_diff_content.html.erb +16 -0
- data/app/views/observ/prompts/_form.html.erb +111 -0
- data/app/views/observ/prompts/_new_form.html.erb +102 -0
- data/app/views/observ/prompts/_prompt_actions.html.erb +4 -0
- data/app/views/observ/prompts/_prompt_content_highlighted.html.erb +4 -0
- data/app/views/observ/prompts/_version_actions.html.erb +40 -0
- data/app/views/observ/prompts/compare.html.erb +155 -0
- data/app/views/observ/prompts/edit.html.erb +17 -0
- data/app/views/observ/prompts/index.html.erb +108 -0
- data/app/views/observ/prompts/new.html.erb +17 -0
- data/app/views/observ/prompts/show.html.erb +138 -0
- data/app/views/observ/prompts/versions.html.erb +87 -0
- data/app/views/observ/sessions/annotations_drawer.turbo_stream.erb +25 -0
- data/app/views/observ/sessions/drawer_test.turbo_stream.erb +49 -0
- data/app/views/observ/sessions/index.html.erb +91 -0
- data/app/views/observ/sessions/show.html.erb +251 -0
- data/app/views/observ/traces/add_to_dataset_drawer.turbo_stream.erb +48 -0
- data/app/views/observ/traces/annotations_drawer.turbo_stream.erb +25 -0
- data/app/views/observ/traces/index.html.erb +87 -0
- data/app/views/observ/traces/show.html.erb +285 -0
- data/app/views/observ/traces/text_output_drawer.turbo_stream.erb +48 -0
- data/app/views/shared/_drawer.html.erb +26 -0
- data/config/routes.rb +80 -0
- data/db/migrate/001_create_observ_sessions.rb +21 -0
- data/db/migrate/002_create_observ_traces.rb +25 -0
- data/db/migrate/003_create_observ_observations.rb +42 -0
- data/db/migrate/004_add_message_id_to_observ_traces.rb +7 -0
- data/db/migrate/005_create_observ_prompts.rb +21 -0
- data/db/migrate/006_fix_prompt_config_strings.rb +23 -0
- data/db/migrate/007_create_observ_annotations.rb +12 -0
- data/db/migrate/009_add_prompt_fields_to_observ_chats.rb +11 -0
- data/db/migrate/010_create_observ_datasets.rb +15 -0
- data/db/migrate/011_create_observ_dataset_items.rb +17 -0
- data/db/migrate/012_create_observ_dataset_runs.rb +22 -0
- data/db/migrate/013_create_observ_dataset_run_items.rb +16 -0
- data/db/migrate/014_create_observ_scores.rb +26 -0
- data/lib/generators/observ/add_phase_tracking/add_phase_tracking_generator.rb +150 -0
- data/lib/generators/observ/add_phase_tracking/templates/migration.rb.tt +6 -0
- data/lib/generators/observ/install/USAGE +27 -0
- data/lib/generators/observ/install/install_generator.rb +270 -0
- data/lib/generators/observ/install_chat/install_chat_generator.rb +313 -0
- data/lib/generators/observ/install_chat/templates/agents/base_agent.rb.tt +147 -0
- data/lib/generators/observ/install_chat/templates/agents/simple_agent.rb.tt +55 -0
- data/lib/generators/observ/install_chat/templates/concerns/observ_chat_enhancements.rb.tt +34 -0
- data/lib/generators/observ/install_chat/templates/concerns/observ_message_enhancements.rb.tt +18 -0
- data/lib/generators/observ/install_chat/templates/initializers/observability.rb.tt +20 -0
- data/lib/generators/observ/install_chat/templates/jobs/chat_response_job.rb.tt +56 -0
- data/lib/generators/observ/install_chat/templates/migrations/add_agent_class_name.rb.tt +6 -0
- data/lib/generators/observ/install_chat/templates/migrations/add_observability_session_id.rb.tt +6 -0
- data/lib/generators/observ/install_chat/templates/tools/think_tool.rb.tt +29 -0
- data/lib/generators/observ/install_chat/templates/views/messages/_content.html.erb.tt +1 -0
- data/lib/observ/asset_installer.rb +130 -0
- data/lib/observ/asset_syncer.rb +104 -0
- data/lib/observ/configuration.rb +108 -0
- data/lib/observ/engine.rb +50 -0
- data/lib/observ/index_file_generator.rb +142 -0
- data/lib/observ/instrumenter/ruby_llm.rb +6 -0
- data/lib/observ/version.rb +3 -0
- data/lib/observ.rb +29 -0
- data/lib/tasks/observ_tasks.rake +75 -0
- metadata +453 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require "bundler/setup"
|
|
2
|
+
require "bundler/gem_tasks"
|
|
3
|
+
|
|
4
|
+
# Load Rails tasks only when Rails is available
|
|
5
|
+
if defined?(Rails)
|
|
6
|
+
load "rails/tasks/statistics.rake"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
namespace :gem do
|
|
10
|
+
desc "Build and install gem locally"
|
|
11
|
+
task :install_local do
|
|
12
|
+
Rake::Task["build"].invoke
|
|
13
|
+
gem_file = Dir["pkg/*.gem"].sort_by { |f| File.mtime(f) }.last
|
|
14
|
+
sh "gem install #{gem_file}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc "Build, tag, and push gem to RubyGems"
|
|
18
|
+
task :publish do
|
|
19
|
+
require_relative "lib/observ/version"
|
|
20
|
+
version = Observ::VERSION
|
|
21
|
+
|
|
22
|
+
puts "Publishing observ v#{version}..."
|
|
23
|
+
|
|
24
|
+
# Build the gem
|
|
25
|
+
puts "\n1. Building gem..."
|
|
26
|
+
Rake::Task["build"].invoke
|
|
27
|
+
|
|
28
|
+
# Tag the release
|
|
29
|
+
puts "\n2. Creating git tag v#{version}..."
|
|
30
|
+
sh "git tag -a v#{version} -m 'Release v#{version}'" rescue puts "Tag already exists"
|
|
31
|
+
|
|
32
|
+
# Push gem to RubyGems
|
|
33
|
+
puts "\n3. Pushing to RubyGems..."
|
|
34
|
+
gem_file = "pkg/observ-#{version}.gem"
|
|
35
|
+
sh "gem push #{gem_file}"
|
|
36
|
+
|
|
37
|
+
# Push tags to remote
|
|
38
|
+
puts "\n4. Pushing tags to remote..."
|
|
39
|
+
sh "git push origin --tags"
|
|
40
|
+
|
|
41
|
+
puts "\n✓ Successfully published observ v#{version}!"
|
|
42
|
+
puts " View at: https://rubygems.org/gems/observ"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
desc "Clean built gems"
|
|
46
|
+
task :clean do
|
|
47
|
+
sh "rm -f *.gem"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Observ Engine JavaScript
|
|
2
|
+
// This file is automatically included when the engine layout is rendered
|
|
3
|
+
|
|
4
|
+
// Import Stimulus if available
|
|
5
|
+
if (typeof Stimulus !== 'undefined') {
|
|
6
|
+
console.log('Observ: Stimulus detected');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Observ initialization
|
|
10
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
11
|
+
console.log('Observ engine loaded');
|
|
12
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["container"]
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
console.log('[Observ] autoscroll controller connected')
|
|
8
|
+
this.scrollToBottom()
|
|
9
|
+
this.observer = new MutationObserver(() => {
|
|
10
|
+
this.scrollToBottom()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
this.observer.observe(this.containerTarget, {
|
|
14
|
+
childList: true,
|
|
15
|
+
subtree: true
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
disconnect() {
|
|
20
|
+
if (this.observer) {
|
|
21
|
+
this.observer.disconnect()
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
scrollToBottom() {
|
|
26
|
+
requestAnimationFrame(() => {
|
|
27
|
+
const lastMessage = this.containerTarget.lastElementChild
|
|
28
|
+
if (lastMessage) {
|
|
29
|
+
lastMessage.scrollIntoView({ behavior: "smooth", block: "start" })
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
// Connects to data-controller="observ--chat-form"
|
|
4
|
+
export default class extends Controller {
|
|
5
|
+
static targets = ["agentSelect", "promptVersionGroup", "promptVersionSelect"]
|
|
6
|
+
static values = {
|
|
7
|
+
promptsUrl: String,
|
|
8
|
+
agentsWithPrompts: Object // Map of agent_name => prompt_name
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
connect() {
|
|
12
|
+
console.log('[Observ] chat-form controller connected')
|
|
13
|
+
console.log(' - Prompts URL:', this.promptsUrlValue)
|
|
14
|
+
console.log(' - Agents with prompts:', this.agentsWithPromptsValue)
|
|
15
|
+
this.togglePromptVersionField()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
disconnect() {
|
|
19
|
+
console.log('[Observ] chat-form controller disconnected')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
agentChanged() {
|
|
23
|
+
console.log('[Observ] Agent selection changed')
|
|
24
|
+
this.togglePromptVersionField()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
togglePromptVersionField() {
|
|
28
|
+
if (!this.hasAgentSelectTarget) {
|
|
29
|
+
console.warn('[Observ] Agent select target not found')
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const selectedAgent = this.agentSelectTarget.value
|
|
34
|
+
console.log(' - Selected agent:', selectedAgent)
|
|
35
|
+
|
|
36
|
+
const promptName = this.agentsWithPromptsValue[selectedAgent]
|
|
37
|
+
console.log(' - Prompt name for agent:', promptName)
|
|
38
|
+
|
|
39
|
+
if (promptName) {
|
|
40
|
+
this.loadPromptVersions(promptName)
|
|
41
|
+
this.showPromptVersionField()
|
|
42
|
+
} else {
|
|
43
|
+
this.hidePromptVersionField()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
loadPromptVersions(promptName) {
|
|
48
|
+
const url = `${this.promptsUrlValue}/${promptName}/versions.json`
|
|
49
|
+
console.log(' - Fetching versions from:', url)
|
|
50
|
+
|
|
51
|
+
fetch(url)
|
|
52
|
+
.then(response => {
|
|
53
|
+
console.log(' - Response status:', response.status)
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
throw new Error(`Failed to fetch prompt versions: ${response.status}`)
|
|
56
|
+
}
|
|
57
|
+
return response.json()
|
|
58
|
+
})
|
|
59
|
+
.then(data => {
|
|
60
|
+
console.log(' - Versions loaded:', data.length, 'version(s)')
|
|
61
|
+
this.populateVersions(data)
|
|
62
|
+
})
|
|
63
|
+
.catch(error => {
|
|
64
|
+
console.error('[Observ] Error loading prompt versions:', error)
|
|
65
|
+
// On error, hide the field gracefully
|
|
66
|
+
this.hidePromptVersionField()
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
populateVersions(versions) {
|
|
71
|
+
this.promptVersionSelectTarget.innerHTML = this.buildVersionOptions(versions)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
buildVersionOptions(versions) {
|
|
75
|
+
const defaultOption = '<option value="">Use default (production)</option>'
|
|
76
|
+
const versionOptions = versions.map(v => {
|
|
77
|
+
const commitMsg = v.commit_message ? ` - ${v.commit_message}` : ''
|
|
78
|
+
return `<option value="${v.version}">v${v.version} - ${v.state}${commitMsg}</option>`
|
|
79
|
+
}).join('')
|
|
80
|
+
|
|
81
|
+
return defaultOption + versionOptions
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
showPromptVersionField() {
|
|
85
|
+
this.promptVersionGroupTarget.classList.remove('observ-hidden')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
hidePromptVersionField() {
|
|
89
|
+
this.promptVersionGroupTarget.classList.add('observ-hidden')
|
|
90
|
+
// Clear the select value when hiding
|
|
91
|
+
this.promptVersionSelectTarget.value = ''
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["source", "button"]
|
|
5
|
+
static classes = ["success"]
|
|
6
|
+
static values = {
|
|
7
|
+
successDuration: { type: Number, default: 2000 }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
connect() {
|
|
11
|
+
console.log('[Observ] copy controller connected')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
copy(event) {
|
|
15
|
+
event.preventDefault()
|
|
16
|
+
|
|
17
|
+
const text = this.sourceTarget.textContent || this.sourceTarget.value
|
|
18
|
+
|
|
19
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
20
|
+
this.showSuccess()
|
|
21
|
+
}).catch(err => {
|
|
22
|
+
console.error('Failed to copy text: ', err)
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
showSuccess() {
|
|
27
|
+
const originalText = this.buttonTarget.textContent
|
|
28
|
+
|
|
29
|
+
this.buttonTarget.textContent = "Copied!"
|
|
30
|
+
|
|
31
|
+
if (this.hasSuccessClass) {
|
|
32
|
+
this.buttonTarget.classList.add(...this.successClasses)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
setTimeout(() => {
|
|
36
|
+
this.buttonTarget.textContent = originalText
|
|
37
|
+
|
|
38
|
+
if (this.hasSuccessClass) {
|
|
39
|
+
this.buttonTarget.classList.remove(...this.successClasses)
|
|
40
|
+
}
|
|
41
|
+
}, this.successDurationValue)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["metrics"]
|
|
5
|
+
static values = {
|
|
6
|
+
refreshInterval: { type: Number, default: 30000 },
|
|
7
|
+
autoRefresh: { type: Boolean, default: false }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
connect() {
|
|
11
|
+
console.log('[Observ] dashboard controller connected')
|
|
12
|
+
if (this.autoRefreshValue) {
|
|
13
|
+
this.startAutoRefresh()
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
disconnect() {
|
|
18
|
+
this.stopAutoRefresh()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
startAutoRefresh() {
|
|
22
|
+
this.refreshTimer = setInterval(() => {
|
|
23
|
+
this.refresh()
|
|
24
|
+
}, this.refreshIntervalValue)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
stopAutoRefresh() {
|
|
28
|
+
if (this.refreshTimer) {
|
|
29
|
+
clearInterval(this.refreshTimer)
|
|
30
|
+
this.refreshTimer = null
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async refresh() {
|
|
35
|
+
try {
|
|
36
|
+
const response = await fetch('/observ/dashboard/metrics', {
|
|
37
|
+
headers: {
|
|
38
|
+
'Accept': 'application/json'
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
throw new Error('Network response was not ok')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const data = await response.json()
|
|
47
|
+
this.updateMetrics(data)
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('Failed to refresh metrics:', error)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
updateMetrics(data) {
|
|
54
|
+
if (this.hasMetricsTarget) {
|
|
55
|
+
console.log('Metrics updated:', data)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["drawer", "content", "headerTitle"]
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
console.log('[Observ] drawer controller connected')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
open(event) {
|
|
11
|
+
event.preventDefault()
|
|
12
|
+
const url = event.currentTarget.dataset.drawerUrlParam
|
|
13
|
+
|
|
14
|
+
if (!url) {
|
|
15
|
+
console.error("No URL provided for drawer")
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
this.drawerTarget.classList.add("open")
|
|
20
|
+
this.replaceSpinners()
|
|
21
|
+
this.fetchContent(url)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
close(event) {
|
|
25
|
+
if (event) {
|
|
26
|
+
event.preventDefault()
|
|
27
|
+
}
|
|
28
|
+
this.drawerTarget.classList.remove("open")
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
replaceSpinners() {
|
|
32
|
+
this.headerTitleTarget.innerHTML = '<span class="observ-spinner"></span>'
|
|
33
|
+
this.contentTarget.innerHTML = '<div class="observ-drawer__loading"><span class="observ-spinner"></span></div>'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async fetchContent(url) {
|
|
37
|
+
try {
|
|
38
|
+
const response = await fetch(url, {
|
|
39
|
+
headers: {
|
|
40
|
+
Accept: "text/vnd.turbo-stream.html"
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
throw new Error(`HTTP error! status: ${response.status}`)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const html = await response.text()
|
|
49
|
+
const turboStream = document.createElement("div")
|
|
50
|
+
turboStream.innerHTML = html
|
|
51
|
+
document.body.appendChild(turboStream)
|
|
52
|
+
setTimeout(() => turboStream.remove(), 100)
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error("Failed to fetch drawer content:", error)
|
|
55
|
+
this.contentTarget.innerHTML = '<div class="observ-drawer__error">Failed to load content</div>'
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["content", "toggle"]
|
|
5
|
+
static classes = ["expanded"]
|
|
6
|
+
static values = {
|
|
7
|
+
expanded: { type: Boolean, default: false }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
connect() {
|
|
11
|
+
console.log('[Observ] expandable controller connected')
|
|
12
|
+
this.updateState()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
toggle() {
|
|
16
|
+
this.expandedValue = !this.expandedValue
|
|
17
|
+
this.updateState()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
updateState() {
|
|
21
|
+
if (this.expandedValue) {
|
|
22
|
+
this.contentTarget.classList.remove("hidden")
|
|
23
|
+
if (this.hasToggleTarget) {
|
|
24
|
+
this.toggleTarget.textContent = "Collapse ▲"
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
this.contentTarget.classList.add("hidden")
|
|
28
|
+
if (this.hasToggleTarget) {
|
|
29
|
+
this.toggleTarget.textContent = "Expand ▼"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["form", "input"]
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
console.log('[Observ] filter controller connected')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
submit(event) {
|
|
11
|
+
event.preventDefault()
|
|
12
|
+
this.formTarget.requestSubmit()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
clear(event) {
|
|
16
|
+
event.preventDefault()
|
|
17
|
+
|
|
18
|
+
this.inputTargets.forEach(input => {
|
|
19
|
+
if (input.type === "text" || input.type === "date") {
|
|
20
|
+
input.value = ""
|
|
21
|
+
} else if (input.type === "select-one") {
|
|
22
|
+
input.selectedIndex = 0
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
this.formTarget.requestSubmit()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
autoSubmit() {
|
|
30
|
+
clearTimeout(this.timeout)
|
|
31
|
+
|
|
32
|
+
this.timeout = setTimeout(() => {
|
|
33
|
+
this.formTarget.requestSubmit()
|
|
34
|
+
}, 500)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Auto-generated index file for Observ Stimulus controllers
|
|
2
|
+
// Register all Observ controllers with the observ-- prefix
|
|
3
|
+
//
|
|
4
|
+
// This file is designed to be imported by the host application's Stimulus setup.
|
|
5
|
+
// The host app should import this file in their controllers/index.js:
|
|
6
|
+
// import "./observ"
|
|
7
|
+
|
|
8
|
+
import AutoscrollController from "./autoscroll_controller.js"
|
|
9
|
+
import ChatFormController from "./chat_form_controller.js"
|
|
10
|
+
import CopyController from "./copy_controller.js"
|
|
11
|
+
import DashboardController from "./dashboard_controller.js"
|
|
12
|
+
import DrawerController from "./drawer_controller.js"
|
|
13
|
+
import ExpandableController from "./expandable_controller.js"
|
|
14
|
+
import FilterController from "./filter_controller.js"
|
|
15
|
+
import JsonViewerController from "./json_viewer_controller.js"
|
|
16
|
+
import MessageFormController from "./message_form_controller.js"
|
|
17
|
+
import PromptVariablesController from "./prompt_variables_controller.js"
|
|
18
|
+
import TextSelectController from "./text_select_controller.js"
|
|
19
|
+
|
|
20
|
+
// Export controllers for manual registration if needed
|
|
21
|
+
export {
|
|
22
|
+
AutoscrollController,
|
|
23
|
+
ChatFormController,
|
|
24
|
+
CopyController,
|
|
25
|
+
DashboardController,
|
|
26
|
+
DrawerController,
|
|
27
|
+
ExpandableController,
|
|
28
|
+
FilterController,
|
|
29
|
+
JsonViewerController,
|
|
30
|
+
MessageFormController,
|
|
31
|
+
PromptVariablesController,
|
|
32
|
+
TextSelectController
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Auto-register if Stimulus application is available globally
|
|
36
|
+
if (typeof window.Stimulus !== "undefined") {
|
|
37
|
+
const application = window.Stimulus
|
|
38
|
+
|
|
39
|
+
application.register("observ--autoscroll", AutoscrollController)
|
|
40
|
+
application.register("observ--chat-form", ChatFormController)
|
|
41
|
+
application.register("observ--copy", CopyController)
|
|
42
|
+
application.register("observ--dashboard", DashboardController)
|
|
43
|
+
application.register("observ--drawer", DrawerController)
|
|
44
|
+
application.register("observ--expandable", ExpandableController)
|
|
45
|
+
application.register("observ--filter", FilterController)
|
|
46
|
+
application.register("observ--json-viewer", JsonViewerController)
|
|
47
|
+
application.register("observ--message-form", MessageFormController)
|
|
48
|
+
application.register("observ--prompt-variables", PromptVariablesController)
|
|
49
|
+
application.register("observ--text-select", TextSelectController)
|
|
50
|
+
|
|
51
|
+
console.log("Observ Stimulus controllers registered")
|
|
52
|
+
}
|