Class: Get::Smart::Ai::Generator

Inherits:
Object
  • Object
show all
Defined in:
lib/get/smart/ai/generator.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(topic, count: 10, level: "all", folder:) ⇒ Generator

Returns a new instance of Generator.



9
10
11
12
13
14
# File 'lib/get/smart/ai/generator.rb', line 9

def initialize(topic, count: 10, level: "all", folder:)
  @topic = topic
  @count = count
  @level = level
  @folder = folder
end

Instance Attribute Details

#countObject (readonly)

Returns the value of attribute count.



7
8
9
# File 'lib/get/smart/ai/generator.rb', line 7

def count
  @count
end

#folderObject (readonly)

Returns the value of attribute folder.



7
8
9
# File 'lib/get/smart/ai/generator.rb', line 7

def folder
  @folder
end

#levelObject (readonly)

Returns the value of attribute level.



7
8
9
# File 'lib/get/smart/ai/generator.rb', line 7

def level
  @level
end

#topicObject (readonly)

Returns the value of attribute topic.



7
8
9
# File 'lib/get/smart/ai/generator.rb', line 7

def topic
  @topic
end

Instance Method Details

#callObject



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/get/smart/ai/generator.rb', line 16

def call
  tips = response["tips"] || response
  tips.each do |tip|
    # https://github.com/fazibear/colorize/blob/master/lib/colorize/class_methods.rb
    5.times { puts }
    print "[#{tip["filename"]}]".on_magenta.black
    print " - "
    print "[#{tip["level"]}]".blue
    2.times { puts }
    puts TTY::Markdown.parse(tip["content"], indent: 0)
    2.times { puts }
    # for development & testing
    # File.write(File.expand_path("../../../../../tmp/#{tip["filename"].to_s}.md", __FILE__), tip["content"])

    # for production
    file_path = "#{folder}/#{tip["level"]}/#{tip["filename"]}.md"
    puts "Writing to #{file_path}".on_yellow.black
    File.write(File.expand_path(file_path, __FILE__), tip["content"])
  end
  puts
  puts "Created #{tips.count} tips".green
  puts "Done!".green
rescue => e
  puts "==" * 40
  puts response
  puts "==" * 40
  raise e
end

#examplesObject



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/get/smart/ai/generator.rb', line 161

def examples
  examples = [
    {
      "filename": "string_interpolation",
      "content": File.expand_path("../../../../../spec/files/other/middle/file1.md", __FILE__),
      "level": "advanced"
    },
    {
      "filename": "string_squeeze",
      "content": File.expand_path("../../../../../spec/files/other/middle/file2.md", __FILE__),
      "level": "middle"
    }
  ]

  <<~EXAMPLES
    Example 1:
    ```json
    #{{ tips: examples }.to_json}
    ```
  EXAMPLES
end

#instructionsObject



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/get/smart/ai/generator.rb', line 70

def instructions
  <<~INSTRUCTIONS
  You are a helpful assistant that generates concise, practical Ruby on Rails tips in JSON format.

  <GOAL>
    Your goal is to generate #{count} tips for the given topic with the following level: #{level}.
  </GOAL>

  Requirements:
  - Input: a single `<TOPIC>...</TOPIC>` tag specifying the focus.
  - Output: a JSON object matching this schema:
    {
      "tips": [
        {
          "filename": "unique_string",
          "content": "markdown_tip",
          "level": "beginner" | "middle" | "advanced" | "expert"
        },
        ...
      ]
    }
  - For each tip object:
    - **filename**: a unique, descriptive identifier (no duplicates).
    - **content**: markdown starting with a level-2 heading:
      `## {emoji} Tip Title`
      - Use a Unicode emoji followed by a space before the title.
      - Write a description (why, what, and how; 2-4 sentences or even more if needed) and include one or more code examples.
      - Add a blank line before each code block.
      - Specify the language for every code block.
      - send complete tip, not just code, make sure it is including all the information needed to implement the tip
      - always block of code with ```.
      - always add a new line after and before the code block.
    - **level**: one of `"beginner"`, `"middle"`, `"advanced"`, or `"expert"`, indicating the target audience.
  - Control tip count and levels:
    - Generate at least **{count}** tips. If `count` is 0, generate as many as you can.
    - If `level` is `"all"`, include tips across all levels; otherwise, only that level.
    - Favor towards more advanced tips to challenge experienced developers if `level` is not specified.
    - for "expert" level add super complex, strong, advanced tips, for developers with 8+ years of experience.
    - when "level" is specified, generate only that level of tips. For example, if level is "expert", generate only expert tips.
  - Keep tips short, actionable, and focused strictly on the provided topic.
  - Do not include any extra fields or properties.
  - Return only the JSON object—no additional text or commentary.
  - You must follow the examples and instructions strictly.

  <EXAMPLES>
    #{examples}
  </EXAMPLES>
  INSTRUCTIONS
end

#promptObject



64
65
66
67
68
# File 'lib/get/smart/ai/generator.rb', line 64

def prompt
  <<~PROMPT
    <TOPIC>#{topic}</TOPIC>
  PROMPT
end

#responseObject



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/get/smart/ai/generator.rb', line 45

def response
  @response ||= begin
    res = client.chat(
      parameters: {
        # https://platform.openai.com/docs/models
        model: "o4-mini",
        temperature: 1,
        messages: [
          { role: "assistant", content: instructions },
          { role: "user", content: prompt }
        ],
        response_format: response_format
      }
    )
  end

  JSON.parse(res.dig("choices", 0, "message", "content"))
end

#response_formatObject

Open AI JSON Schema



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/get/smart/ai/generator.rb', line 121

def response_format
  {
    type: "json_schema",
    json_schema: {
      name: "tips",
      strict: true,
      schema: {
        type: "object",
        properties: {
          tips: {
            type: "array",
            items: {
              type: "object",
              properties: {
                filename: {
                  type: "string",
                  description: "unique filename to store on disk, about content inside"
                },
                content: {
                  type: "string",
                  description: "tip content, in markdown format"
                },
                level: {
                  type: "string",
                  enum: [ "beginner", "middle", "advanced", "expert" ],
                  description: "level of the tip for the target audience"
                }
              },
              required: [ "filename", "content", "level" ],
              additionalProperties: false
            }
          }
        },
        required: [ "tips" ],
        additionalProperties: false
      }
    }
  }
end