Skip to content

jdkim/prompt_manager

Repository files navigation

PromptNavigator

A Rails engine for managing and visualizing LLM prompt execution history. It provides a visual history stack UI with interactive SVG arrow connections between parent-child prompt executions, enabling users to navigate conversation trees.

Features

  • PromptExecution model - Self-referencing tree structure for tracking prompt executions with UUID identifiers
  • Visual history stack - Card-based UI displaying prompt history with parent-child relationships
  • Arrow visualization - Straight arrows () for adjacent cards; curved SVG arrows (via Stimulus) for non-adjacent parent-child connections
  • Active state highlighting - Blue border and glow effect on the currently selected card
  • Responsive design - Hover effects with lift animation; arrows redraw on window resize
  • Automatic integration - Controller concern, view helpers, and assets are auto-registered by the engine
  • Rails generator - prompt_navigator:modeling generator for creating the required migration

Requirements

  • Ruby >= 3.2.0
  • Rails >= 8.1.2
  • Stimulus (Hotwire)

Installation

Add this line to your application's Gemfile:

gem "prompt_navigator"

And then execute:

$ bundle install

Generate the migration for prompt executions:

$ rails generate prompt_navigator:modeling
$ rails db:migrate

This creates the prompt_navigator_prompt_executions table with the following columns:

Column Type Description
execution_id string Unique identifier (UUID), auto-generated on create
prompt text The prompt text sent to the LLM
llm_platform string The LLM platform (e.g., "openai", "anthropic")
model string The model name (e.g., "gpt-4", "claude-3")
configuration string Model configuration as JSON
response text The LLM response text
previous_id integer Foreign key to the parent execution (for building history tree)

Usage

Layout Setup

In your application layout (app/views/layouts/application.html.erb), add <%= yield :head %> in the <head> section to load the engine's stylesheets:

<head>
  <title>Your App</title>
  <%= csrf_meta_tags %>
  <%= csp_meta_tag %>

  <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
  <%= javascript_importmap_tags %>

  <%= yield :head %>
</head>

Controller Setup

The HistoryManageable concern is automatically included in all controllers by the engine. It provides three methods:

  • initialize_history(history) - Sets the @history instance variable (converts to array)
  • set_active_message_uuid(uuid) - Sets the @active_message_uuid instance variable
  • push_to_history(new_state) - Appends a new state to @history
class MyController < ApplicationController
  def index
    # History must be ordered newest-first for arrow visualization to work correctly
    initialize_history(PromptNavigator::PromptExecution.order(id: :desc))
    set_active_message_uuid(params[:execution_id])
  end

  def create
    new_execution = PromptNavigator::PromptExecution.create(
      prompt: params[:prompt],
      llm_platform: "openai",
      model: "gpt-4",
      configuration: params[:config].to_json,
      response: llm_response,
      previous: @current_execution
    )
    push_to_history(new_execution)
  end
end

Rendering the History Component

The history_list helper is automatically available in all views. It renders the history stack:

<%= history_list(
  ->(execution_id) { my_item_path(execution_id) },
  active_uuid: @active_message_uuid
) %>

Parameters:

  • card_path (first argument) - A Proc/Lambda that takes an execution_id and returns a URL path for the card link
  • active_uuid: - The execution_id of the currently active card (highlighted with blue border)

Alternatively, you can render the partial directly:

<%= render "prompt_navigator/history",
    locals: {
      active_uuid: @active_message_uuid,
      card_path: ->(execution_id) { my_item_path(execution_id) }
    }
%>

PromptExecution Model

The PromptNavigator::PromptExecution model stores prompt execution records with a self-referencing association for building conversation trees:

# Create an execution
execution = PromptNavigator::PromptExecution.create(
  prompt: "Explain Ruby blocks",
  llm_platform: "openai",
  model: "gpt-4",
  configuration: '{"temperature": 0.7}',
  response: "Ruby blocks are..."
)

# Create a follow-up execution linked to the parent
follow_up = PromptNavigator::PromptExecution.create(
  prompt: "Give me an example",
  llm_platform: "openai",
  model: "gpt-4",
  response: "Here is an example...",
  previous: execution  # Sets previous_id to link as child
)

# Access attributes
execution.execution_id  # => "a1b2c3d4-..." (auto-generated UUID)
execution.previous      # => nil (root execution)
follow_up.previous      # => execution (parent)

Architecture

PromptNavigator (Rails Engine)
├── Model
│   └── PromptExecution         # Self-referencing tree model with UUID
├── Controller Concern
│   └── HistoryManageable       # Provides initialize_history, set_active_message_uuid, push_to_history
├── View Helper
│   └── Helpers#history_list    # Renders the history partial
├── Partials
│   ├── _history.html.erb       # Main container with Stimulus controller
│   └── _history_card.html.erb  # Individual card with link and arrow logic
├── JavaScript
│   └── history_controller.js   # Stimulus controller for SVG curved arrows
├── Stylesheets
│   └── history.css             # Modern nested CSS for cards, arrows, hover effects
└── Generator
    └── modeling                # Creates migration for prompt_executions table

Arrow Visualization

The history component uses two types of arrows to show parent-child relationships:

  1. Straight arrows () - Rendered as HTML when a card's parent is the immediately adjacent card below it in the list
  2. Curved SVG arrows - Drawn by the Stimulus controller (history_controller.js) when a card's parent is further away (vertical gap >= 80px). These bezier curves arc to the left of the stack

Note: Arrow visualization requires the history to be ordered newest-first (e.g., order(id: :desc)). If the history is in ascending order, parent-child adjacency detection will not work correctly.

The Stimulus controller automatically redraws SVG arrows on window resize.

Troubleshooting

Styles not loading

Make sure you have <%= yield :head %> in your application layout's <head> section.

Arrows not appearing

The arrow visualization requires:

  1. Stimulus to be properly configured in your application
  2. The history_controller.js to be loaded (automatic with the asset pipeline)
  3. Parent-child relationships to be set using the previous association on PromptExecution records

History not displaying

Ensure that:

  1. @history is set in your controller using initialize_history
  2. PromptExecution records have execution_id values (automatically generated on create)
  3. The card_path callable returns valid paths

Development

After checking out the repo, run:

$ bundle install

Run the tests:

$ bin/rails test

Run the linter:

$ bundle exec rubocop

Contributing

Bug reports and pull requests are welcome on GitHub.

License

The gem is available as open source under the terms of the MIT License.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors