๐ช Magic
OOLLM: Think it, call it, chain it: Magic lets you call and compose any method you can think of using fluent Ruby.
Setup
- Set
OPENAI_API_KEYto a valid openai key - Install ruby
>= ruby 3.3.4(that's it! no other dependencies)
Usage
- Run tests
ruby test_magic.rbto make sure everything's working properly - Run an interactive session
irb(from the root directory)
Roadmap
- single method execution with and without arguments
- method chaining with and without arguments
- strong types
- recursion
- caching
Try it
$ irb DEBUG is set to true magic is ready ๐ช, type `@magic = Magic.new` to get started >> @magic = Magic.new >> @magic.emoji_getter('happy camper') ๐ฎ Step 1: emoji_getter("happy camper") โ "๐๏ธ๐" => ๐๏ธ๐
Examples
Single Method Calls
@magic = Magic.new result = @magic.random_number(0..1000) result => 746 result.class => Magic # Other examples: @magic.state_capital('Michigan', 'USA') => Lansing @magic.types_of_cheese_in_geo('world') => Cheddar, Mozzarella, Parmesan, Brie, Gouda, Swiss, Blue, Feta, Provolone, Monterey Jack, Camembert, Colby, Havarti, Manchego, Ricotta, Gorgonzola, Gruyรจre, Roquefort, Emmental, Asiago @magic.types_of_cheese_in_geo('france') => Brie, Camembert, Roquefort, Comtรฉ, Reblochon, Munster, Emmental, Beaufort, Cantal, Saint-Nectaire, Pont-lโรvรชque, Livarot, Chรจvre (various goat cheeses), Tomme de Savoie, Bleu dโAuvergne, Bleu de Gex, Boursin, Neufchรขtel, Ossau-Iraty, Morbier
Method Chaining
Magic enables fluent API method chaining. Each method call makes an immediate API request and passes the previous result as context to the next call.
Example 1: Chaining Two Methods
>> result = @magic.random_number.multiply_by(5) ๐ฎ Step 1: random_number โ "271" ๐ฎ Step 2: multiply_by(5) โ "1355" => 1355 puts "History length: #{result.instance_variable_get(:@history).length} steps" # => History length: 2 steps
Example 2: Chaining Three Methods
>> result = @magic.random_number.multiply_by(5).add(10) ๐ฎ Step 1: random_number โ "70628" ๐ฎ Step 2: multiply_by(5) โ "353140" ๐ฎ Step 3: add(10) โ "353150" => 353150
Example 3: Accessing Intermediate Results
>> step1 = @magic.get_number ๐ฎ Step 1: get_number โ "42" => 42 >> puts "Step 1 result: #{step1.result}" Step 1 result: 42 >> step2 = step1.double_it ๐ฎ Step 2: double_it โ "84" => 84 >> step3 = step2.add(100) ๐ฎ Step 3: add(100) โ "184" => 184 >> puts "Full chain inspect: #{step3.inspect}" Full chain inspect: #<Magic history=3 steps, result="184">
Key Features:
- Each method call makes an immediate API request
- Previous result is passed as context to next call
- Magic instances are immutable (functional style)
- Auto-executes on
puts/string interpolation viato_s - Access raw result via
.resultmethod - View chain history via
.inspectmethod
Pipeline Processing & Nested Data Navigation
Magic enables powerful data transformation pipelines through its context-aware chaining. Magic's chaining allows for sequential transformations where each step receives context from previous operations.
Example 1: Data Pipeline Processing
# Transform data through multiple steps >> result = @magic.list_us_presidents >> .take_first(5) >> .get_birthplaces >> .find_common_state ๐ฎ Step 1: list_us_presidents โ "George Washington\nJohn Adams\nThomas Jefferson\nJames Madison\nJames Monroe\nJohn Quincy Adams\nAndrew Jackson\nMartin Van Buren\nWilliam Henry Harrison\nJohn Tyler\nJames K. Polk\nZachary Taylor\nMillard Fillmore\nFranklin Pierce\nJames Buchanan\nAbraham Lincoln\nAndrew Johnson\nUlysses S. Grant\nRutherford B. Hayes\nJames A. Garfield\nChester A. Arthur\nGrover Cleveland\nBenjamin Harrison\nGrover Cleveland\nWilliam McKinley\nTheodore Roosevelt\nWilliam Howard Taft\nWoodrow Wilson\nWarren G. Harding\nCalvin Coolidge\nHerbert Hoover\nFranklin D. Roosevelt\nHarry S. Truman\nDwight D. Eisenhower\nJohn F. Kennedy\nLyndon B. Johnson\nRichard Nixon\nGerald Ford\nJimmy Carter\nRonald Reagan\nGeorge H. W. Bush\nBill Clinton\nGeorge W. Bush\nBarack Obama\nDonald Trump\nJoe Biden" ๐ฎ Step 2: take_first(5) โ "George Washington\nJohn Adams\nThomas Jefferson\nJames Madison\nJames Monroe" ๐ฎ Step 3: get_birthplaces โ "Westmoreland County, Virginia\nBraintree, Massachusetts\nShadwell, Virginia\nPort Conway, Virginia\nWestmoreland County, Virginia" ๐ฎ Step 4: find_common_state โ "Virginia" => Virginia
Example 2: Nested Object Navigation
# Drill down through nested data structures >> result = @magic.countries_in('Europe') >> .get_details('France') >> .largest_city >> .population ๐ฎ Step 1: countries_in("Europe") โ "Albania\nAndorra\nArmenia\nAustria\nAzerbaijan\nBelarus\nBelgium\nBosnia and Herzegovina\nBulgaria\nCroatia\nCyprus\nCzechia\nDenmark\nEstonia\nFinland\nFrance\nGeorgia\nGermany\nGreece\nHungary\nIceland\nIreland\nItaly\nKazakhstan\nKosovo\nLatvia\nLiechtenstein\nLithuania\nLuxembourg\nMalta\nMoldova\nMonaco\nMontenegro\nNetherlands\nNorth Macedonia\nNorway\nPoland\nPortugal\nRomania\nRussia\nSan Marino\nSerbia\nSlovakia\nSlovenia\nSpain\nSweden\nSwitzerland\nTurkey\nUkraine\nUnited Kingdom\nVatican City" ๐ฎ Step 2: get_details("France") โ "Capital: Paris\nPopulation: ~68 million\nOfficial language: French\nGovernment: Unitary semi-presidential republic\nCurrency: Euro (โฌ)\nArea: ~551,700 kmยฒ (metropolitan France)\nContinent/Region: Western Europe\nEU member: Yes (founding member)\nNotable: Largest country in the EU by area; major global center for culture, fashion, cuisine, and wine; key role in EU and international politics." ๐ฎ Step 3: largest_city โ "Paris" ๐ฎ Step 4: population โ "2165423" => 2165423
Example 3: Computational Pipelines
# Chain mathematical operations >> result = @magic.factorial(5) >> .multiply_by(2) >> .add(10) ๐ฎ Step 1: factorial(5) โ "120" ๐ฎ Step 2: multiply_by(2) โ "240" ๐ฎ Step 3: add(10) โ "250" => 250
Example 4: Context-Aware Operations
# Operations that can reference previous context >> result = @magic.number(10) >> .double_it # 10 * 2 = 20 >> .add_previous # 20 + 10 = 30 (LLM can access original context) >> .square # 30^2 = 900 ๐ฎ Step 1: number(10) โ "10" ๐ฎ Step 2: double_it โ "20" ๐ฎ Step 3: add_previous โ "20" ๐ฎ Step 4: square โ "400" => 400
Webserver / example page
There is a tiny example webserver (server.rb) and an ERB template (index.html.erb) that demonstrates embedding Magic output into a web page.
- server:
server.rbโ a small WEBrick server (port 3000) that rendersindex.html.erb. - template:
index.html.erbโ calls@magic.generate_html(...)and inserts the returned HTML into the page.
How to run
# set your OpenAI key first (required) export OPENAI_API_KEY="sk-..." # start the example server ruby server.rb # then open http://localhost:3000 in your browser
What the template does
The index.html.erb file demonstrates a simple call to the Magic object:
<%= @magic.generate_html( tag: 'body', theme: 'wu tang clan', mode: 'dark', generate_content: "fan fiction", looks_like: "wu tang clan" ) %>
The example passes a few options to generate_html โ these are just demonstration inputs (tag, visual theme, content type and style). Magic will return HTML (a string) that the template inserts into the page. This is a minimal demo of using Magic results inside a web UI โ no frameworks or external gems required (only Ruby standard library: WEBrick + ERB).
- The server makes real LLM requests, so ensure
OPENAI_API_KEYis set and be mindful of API usage. - This example is intentionally minimal โ use it as a starting point for building a small experiment or integrating
Magicinto your own web view.
How It Works
Magic's pipeline processing uses sequential execution with context passing:
-
Context Passing: Each chained call receives the previous result in the prompt:
# Previous result is automatically included "Previous result: {previous_value}"
-
Chain History: The
@historyarray maintains a complete audit trail of all operations -
LLM Reasoning: The LLM can reason about nested structures and relationships since it has access to full context
-
Sequential Execution: Each step:
- Executes independently: Makes its own API call
- Receives context: Gets previous result as input
- Passes forward: Sends result to next step
- LLM-powered: Intelligence comes from the LLM understanding data relationships
