docit 0.2.0 → 0.2.1
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/CHANGELOG.md +8 -1
- data/README.md +40 -2
- data/lib/docit/ai/anthropic_client.rb +6 -0
- data/lib/docit/ai/autodoc_runner.rb +24 -8
- data/lib/docit/ai/client.rb +9 -0
- data/lib/docit/ai/groq_client.rb +24 -0
- data/lib/docit/ai/openai_client.rb +6 -0
- data/lib/docit/version.rb +1 -1
- data/lib/docit.rb +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3fe9c345834352ce5e4219a291760ced965cdbad10084c28cbc2aabc2823a5b5
|
|
4
|
+
data.tar.gz: 86aa4599733b72c52fe66335c4581e4f69b408af1bbb00c0c2d5f42b1b7a51fe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9ad86a75a50a1a1177cf7cbde2bf49f12eb7cc36e1df9fd268ff2b6519c7b01efffc680aab2484a83f3dbdfb410740633a7cabbf8072d873403deafc6c14ccd3
|
|
7
|
+
data.tar.gz: 2fbf560890376ef66614e12f961496b77ab5bfb2e994ffaeb006cc2a5ddd9ef82128115cf7d1fba39a89a889b0aec8342d487525eedad276a9b8244a6ee574d3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.1] - 2026-04-11
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Engine autoloading: `Docit::Engine` is now properly required when Rails is present, fixing `uninitialized constant Docit::Engine` and `Could not find generator 'docit:install'` in consuming apps
|
|
7
|
+
- AI provider clients (OpenAI, Anthropic, Groq) now raise `Docit::Ai::RateLimitError` on 429 responses with parsed retry-after timing
|
|
8
|
+
- `AutodocRunner` now retries rate-limited requests up to 3 times with exponential backoff (capped at 5 minutes) instead of failing immediately
|
|
9
|
+
|
|
3
10
|
## [0.2.0] - 2026-04-11
|
|
4
11
|
|
|
5
12
|
### Added
|
|
@@ -30,7 +37,7 @@
|
|
|
30
37
|
|
|
31
38
|
- Initial release
|
|
32
39
|
- DSL: `swagger_doc` macro for inline controller documentation
|
|
33
|
-
- DSL: `use_docs` + `Docit::DocFile` for separate doc files
|
|
40
|
+
- DSL: `use_docs` + `Docit::DocFile` for separate doc files
|
|
34
41
|
- Builders: request body, response, and parameter builders with nested object/array support
|
|
35
42
|
- Schema `$ref` components via `Docit.define_schema`
|
|
36
43
|
- File upload support (`type: :file` → `string/binary`)
|
data/README.md
CHANGED
|
@@ -5,7 +5,43 @@
|
|
|
5
5
|
|
|
6
6
|
Decorator-style API documentation for Ruby on Rails. Write OpenAPI 3.0.3 docs with clean controller DSL macros, separate doc modules, or AI-assisted scaffolding for undocumented endpoints.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## Table Of Contents
|
|
9
|
+
|
|
10
|
+
- Getting started
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
- [Configuration](#configuration)
|
|
13
|
+
- [Usage](#usage)
|
|
14
|
+
- Documentation styles
|
|
15
|
+
- [Style 1: Inline (simple APIs)](#style-1-inline-simple-apis)
|
|
16
|
+
- [Style 2: Separate doc files (recommended for larger APIs)](#style-2-separate-doc-files-recommended-for-larger-apis)
|
|
17
|
+
- Endpoint DSL reference
|
|
18
|
+
- [Endpoint documentation DSL](#endpoint-documentation-dsl)
|
|
19
|
+
- [Request bodies](#request-bodies)
|
|
20
|
+
- [Path parameters](#path-parameters)
|
|
21
|
+
- [Enums](#enums)
|
|
22
|
+
- [Security](#security)
|
|
23
|
+
- [Deprecated endpoints](#deprecated-endpoints)
|
|
24
|
+
- [Nested objects and arrays](#nested-objects-and-arrays)
|
|
25
|
+
- [Response examples](#response-examples)
|
|
26
|
+
- [Shared schemas (`$ref`)](#shared-schemas-ref)
|
|
27
|
+
- [File uploads](#file-uploads)
|
|
28
|
+
- AI documentation
|
|
29
|
+
- [AI Automatic Documentation](#ai-automatic-documentation)
|
|
30
|
+
- [Quick start (included in install)](#quick-start-included-in-install)
|
|
31
|
+
- [Standalone commands](#standalone-commands)
|
|
32
|
+
- [Supported providers](#supported-providers)
|
|
33
|
+
- [What the AI generates](#what-the-ai-generates)
|
|
34
|
+
- Runtime and development
|
|
35
|
+
- [How it works](#how-it-works)
|
|
36
|
+
- [Mounting at a different path](#mounting-at-a-different-path)
|
|
37
|
+
- [JSON spec only](#json-spec-only)
|
|
38
|
+
- [Development](#development)
|
|
39
|
+
- [Contributing](#contributing)
|
|
40
|
+
- [License](#license)
|
|
41
|
+
- Project docs
|
|
42
|
+
- [CHANGELOG](CHANGELOG.md)
|
|
43
|
+
- [CONTRIBUTING](CONTRIBUTING.md)
|
|
44
|
+
- [CODE OF CONDUCT](CODE_OF_CONDUCT.md)
|
|
9
45
|
|
|
10
46
|
## Installation
|
|
11
47
|
|
|
@@ -85,7 +121,7 @@ end
|
|
|
85
121
|
|
|
86
122
|
### Style 2: Separate doc files (recommended for larger APIs)
|
|
87
123
|
|
|
88
|
-
Keep controllers clean by defining docs in dedicated files
|
|
124
|
+
Keep controllers clean by defining docs in dedicated files:
|
|
89
125
|
|
|
90
126
|
```ruby
|
|
91
127
|
# app/docs/api/v1/users_docs.rb
|
|
@@ -414,6 +450,8 @@ DRY_RUN=1 rails docit:autodoc
|
|
|
414
450
|
| Anthropic | Requires API key from console.anthropic.com |
|
|
415
451
|
| Groq | Free tier at console.groq.com |
|
|
416
452
|
|
|
453
|
+
All providers automatically retry on rate-limit (429) errors with exponential backoff, so free-tier usage works out of the box.
|
|
454
|
+
|
|
417
455
|
AI configuration is stored in `.docit_ai.yml`.
|
|
418
456
|
If your app does not have a `.gitignore`, add `.docit_ai.yml` manually.
|
|
419
457
|
|
|
@@ -41,6 +41,12 @@ module Docit
|
|
|
41
41
|
|
|
42
42
|
if response.is_a?(Net::HTTPSuccess) == false
|
|
43
43
|
message = body.dig("error", "message") || "Unknown API error"
|
|
44
|
+
|
|
45
|
+
if response.code == "429"
|
|
46
|
+
retry_after = response["Retry-After"]&.to_f
|
|
47
|
+
raise RateLimitError.new("Anthropic rate limit exceeded", retry_after: retry_after)
|
|
48
|
+
end
|
|
49
|
+
|
|
44
50
|
raise Error, "Anthropic API error (#{response.code}): #{message}"
|
|
45
51
|
end
|
|
46
52
|
|
|
@@ -103,6 +103,7 @@ module Docit
|
|
|
103
103
|
def generate_docs(gaps, config)
|
|
104
104
|
client = Client.for(config)
|
|
105
105
|
generated = Hash.new { |h, k| h[k] = [] }
|
|
106
|
+
max_retries = 3
|
|
106
107
|
|
|
107
108
|
@output.puts "Generating documentation............."
|
|
108
109
|
|
|
@@ -116,14 +117,29 @@ module Docit
|
|
|
116
117
|
end
|
|
117
118
|
|
|
118
119
|
prompt = builder.build
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
retries = 0
|
|
121
|
+
|
|
122
|
+
begin
|
|
123
|
+
doc_block = client.generate(prompt).strip
|
|
124
|
+
doc_block = strip_markdown_fences(doc_block)
|
|
125
|
+
|
|
126
|
+
generated[gap[:controller]] << doc_block
|
|
127
|
+
@results[:generated] += 1
|
|
128
|
+
@output.puts " done"
|
|
129
|
+
rescue Docit::Ai::RateLimitError => e
|
|
130
|
+
retries += 1
|
|
131
|
+
if retries <= max_retries
|
|
132
|
+
wait = e.retry_after || (2**retries * 10)
|
|
133
|
+
wait = [wait, 300].min # cap at 5 minutes
|
|
134
|
+
@output.puts " rate limited, waiting #{wait.round}s (attempt #{retries}/#{max_retries})..."
|
|
135
|
+
sleep(wait)
|
|
136
|
+
retry
|
|
137
|
+
else
|
|
138
|
+
@output.puts " failed (rate limit exceeded after #{max_retries} retries)"
|
|
139
|
+
end
|
|
140
|
+
rescue Docit::Ai::Error => e
|
|
141
|
+
@output.puts " failed (#{e.message})"
|
|
142
|
+
end
|
|
127
143
|
end
|
|
128
144
|
|
|
129
145
|
generated
|
data/lib/docit/ai/client.rb
CHANGED
|
@@ -4,6 +4,15 @@ module Docit
|
|
|
4
4
|
module Ai
|
|
5
5
|
class Error < Docit::Error; end
|
|
6
6
|
|
|
7
|
+
class RateLimitError < Error
|
|
8
|
+
attr_reader :retry_after
|
|
9
|
+
|
|
10
|
+
def initialize(message, retry_after: nil)
|
|
11
|
+
@retry_after = retry_after
|
|
12
|
+
super(message)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
7
16
|
# Factory for AI provider clients.
|
|
8
17
|
module Client
|
|
9
18
|
def self.for(config)
|
data/lib/docit/ai/groq_client.rb
CHANGED
|
@@ -39,6 +39,12 @@ module Docit
|
|
|
39
39
|
|
|
40
40
|
if response.is_a?(Net::HTTPSuccess) == false
|
|
41
41
|
message = body.dig("error", "message") || "Unknown API error"
|
|
42
|
+
|
|
43
|
+
if response.code == "429"
|
|
44
|
+
retry_after = parse_retry_after(response, message)
|
|
45
|
+
raise RateLimitError.new("Groq rate limit exceeded", retry_after: retry_after)
|
|
46
|
+
end
|
|
47
|
+
|
|
42
48
|
raise Error, "Groq API error (#{response.code}): #{message}"
|
|
43
49
|
end
|
|
44
50
|
|
|
@@ -50,6 +56,24 @@ module Docit
|
|
|
50
56
|
rescue JSON::ParserError
|
|
51
57
|
raise Error, "#{provider_name} returned invalid JSON (HTTP #{response.code})"
|
|
52
58
|
end
|
|
59
|
+
|
|
60
|
+
def parse_retry_after(response, message)
|
|
61
|
+
# Check Retry-After header first (seconds)
|
|
62
|
+
if (header = response["Retry-After"])
|
|
63
|
+
return header.to_f if header.to_f > 0
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Parse "try again in XmY.Zs" from error message
|
|
67
|
+
if message =~ /(\d+)m([\d.]+)s/
|
|
68
|
+
return (Regexp.last_match(1).to_i * 60) + Regexp.last_match(2).to_f
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if message =~ /([\d.]+)s/
|
|
72
|
+
return Regexp.last_match(1).to_f
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
53
77
|
end
|
|
54
78
|
end
|
|
55
79
|
end
|
|
@@ -39,6 +39,12 @@ module Docit
|
|
|
39
39
|
|
|
40
40
|
if response.is_a?(Net::HTTPSuccess) == false
|
|
41
41
|
message = body.dig("error", "message") || "Unknown API error"
|
|
42
|
+
|
|
43
|
+
if response.code == "429"
|
|
44
|
+
retry_after = response["Retry-After"]&.to_f
|
|
45
|
+
raise RateLimitError.new("OpenAI rate limit exceeded", retry_after: retry_after)
|
|
46
|
+
end
|
|
47
|
+
|
|
42
48
|
raise Error, "OpenAI API error (#{response.code}): #{message}"
|
|
43
49
|
end
|
|
44
50
|
|
data/lib/docit/version.rb
CHANGED
data/lib/docit.rb
CHANGED