mathpix 0.1.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/CHANGELOG.md +52 -0
- data/LICENSE +21 -0
- data/README.md +171 -0
- data/SECURITY.md +137 -0
- data/lib/mathpix/balanced_ternary.rb +86 -0
- data/lib/mathpix/batch.rb +155 -0
- data/lib/mathpix/capture_builder.rb +142 -0
- data/lib/mathpix/chemistry.rb +69 -0
- data/lib/mathpix/client.rb +439 -0
- data/lib/mathpix/configuration.rb +187 -0
- data/lib/mathpix/configuration.rb.backup +125 -0
- data/lib/mathpix/conversion.rb +257 -0
- data/lib/mathpix/document.rb +320 -0
- data/lib/mathpix/errors.rb +78 -0
- data/lib/mathpix/mcp/auth/oauth_provider.rb +346 -0
- data/lib/mathpix/mcp/auth/token_manager.rb +31 -0
- data/lib/mathpix/mcp/auth.rb +18 -0
- data/lib/mathpix/mcp/base_tool.rb +117 -0
- data/lib/mathpix/mcp/elicitations/ambiguity_elicitation.rb +162 -0
- data/lib/mathpix/mcp/elicitations/base_elicitation.rb +141 -0
- data/lib/mathpix/mcp/elicitations/confidence_elicitation.rb +162 -0
- data/lib/mathpix/mcp/elicitations.rb +78 -0
- data/lib/mathpix/mcp/middleware/cors_middleware.rb +94 -0
- data/lib/mathpix/mcp/middleware/oauth_middleware.rb +72 -0
- data/lib/mathpix/mcp/middleware/rate_limiting_middleware.rb +140 -0
- data/lib/mathpix/mcp/middleware.rb +13 -0
- data/lib/mathpix/mcp/resources/formats_list_resource.rb +113 -0
- data/lib/mathpix/mcp/resources/hierarchical_router.rb +237 -0
- data/lib/mathpix/mcp/resources/latest_snip_resource.rb +60 -0
- data/lib/mathpix/mcp/resources/recent_snips_resource.rb +75 -0
- data/lib/mathpix/mcp/resources/snip_stats_resource.rb +78 -0
- data/lib/mathpix/mcp/resources.rb +15 -0
- data/lib/mathpix/mcp/server.rb +174 -0
- data/lib/mathpix/mcp/tools/batch_convert_tool.rb +106 -0
- data/lib/mathpix/mcp/tools/check_document_status_tool.rb +66 -0
- data/lib/mathpix/mcp/tools/convert_document_tool.rb +90 -0
- data/lib/mathpix/mcp/tools/convert_image_tool.rb +91 -0
- data/lib/mathpix/mcp/tools/convert_strokes_tool.rb +82 -0
- data/lib/mathpix/mcp/tools/get_account_info_tool.rb +57 -0
- data/lib/mathpix/mcp/tools/get_usage_tool.rb +62 -0
- data/lib/mathpix/mcp/tools/list_formats_tool.rb +81 -0
- data/lib/mathpix/mcp/tools/search_results_tool.rb +111 -0
- data/lib/mathpix/mcp/transports/http_streaming_transport.rb +622 -0
- data/lib/mathpix/mcp/transports/sse_stream_handler.rb +236 -0
- data/lib/mathpix/mcp/transports.rb +12 -0
- data/lib/mathpix/mcp.rb +52 -0
- data/lib/mathpix/result.rb +364 -0
- data/lib/mathpix/version.rb +22 -0
- data/lib/mathpix.rb +229 -0
- metadata +283 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 11a43b1643362801778dbc02b4ec45c3e4cc5bda4dc3b20c191a699c22a67c9d
|
4
|
+
data.tar.gz: 80b902ab983d1a3c1d3a8483646c861edfb77d5ad8b573a66b60d7a0b82cea33
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5b7aa48fd8fd1c983412221f9cf4c06df4a4bb52bd2c217da9125c39f8ebec27755b830457ce6469ee80bba102935987bd686d53a9bfab92ccae30e9264b67d9
|
7
|
+
data.tar.gz: 74aa5937731cb5661d7fecd8354617e6548ddb7d09dd951b2d5559ac9eb9005f620cd12f76c7fe883c77d7f06351f4146c9b5a3128474d715e67c4e3c4a12346
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [0.1.0] - 2025-10-13
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Initial release of Mathpix Ruby client
|
12
|
+
- Core API client with fluent builder pattern
|
13
|
+
- Image OCR support via `/v3/text` endpoint
|
14
|
+
- PDF conversion support via `/v3/pdf` endpoint (async)
|
15
|
+
- Batch processing capabilities with parallel execution
|
16
|
+
- Comprehensive BDD test suite (15+ feature files)
|
17
|
+
- Security-first design:
|
18
|
+
- HTTPS enforcement
|
19
|
+
- Path traversal protection
|
20
|
+
- File size limits (10MB default)
|
21
|
+
- Private IP blocking
|
22
|
+
- Configuration management with validation
|
23
|
+
- Multiple output formats:
|
24
|
+
- LaTeX (styled and standard)
|
25
|
+
- MathML
|
26
|
+
- AsciiMath
|
27
|
+
- Markdown (MMD)
|
28
|
+
- Text
|
29
|
+
- SMILES (chemistry)
|
30
|
+
- Balanced ternary encoding utilities (seed 1069)
|
31
|
+
- Result value objects with domain-specific accessors
|
32
|
+
- Comprehensive error handling hierarchy
|
33
|
+
- MCP (Model Context Protocol) server integration
|
34
|
+
- YARD documentation inline
|
35
|
+
|
36
|
+
### Dependencies
|
37
|
+
- Ruby >= 2.7.0
|
38
|
+
- faraday ~> 2.0 (HTTP client)
|
39
|
+
- concurrent-ruby ~> 1.2 (parallelism)
|
40
|
+
- mcp ~> 0.1.0 (MCP protocol)
|
41
|
+
- rack ~> 3.0 (MCP server)
|
42
|
+
- puma ~> 6.0 (MCP server)
|
43
|
+
|
44
|
+
### Development
|
45
|
+
- cucumber ~> 9.0 (BDD testing)
|
46
|
+
- rspec ~> 3.12 (unit testing)
|
47
|
+
- webmock ~> 3.18 (HTTP mocking)
|
48
|
+
- vcr ~> 6.1 (HTTP recording)
|
49
|
+
- rubocop ~> 1.50 (linting)
|
50
|
+
- bundler-audit ~> 0.9 (security scanning)
|
51
|
+
|
52
|
+
[0.1.0]: https://github.com/teglonlabs/mathpix-mcp-server/releases/tag/v0.1.0
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Barton Rhodes
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
# Mathpix Ruby Client
|
2
|
+
|
3
|
+
[](https://badge.fury.io/rb/mathpix)
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
5
|
+
|
6
|
+
Transform mathematical images to LaTeX, chemistry structures to SMILES, and documents to markdown with security-first design. The geodesic path to mathematical OCR in Ruby.
|
7
|
+
|
8
|
+
## Features
|
9
|
+
|
10
|
+
- 🔒 **Security First**: HTTPS enforcement, path traversal protection, file size limits
|
11
|
+
- 🎯 **Fluent API**: Builder pattern for elegant, chainable operations
|
12
|
+
- ⚡ **Batch Processing**: Parallel execution with callback hooks
|
13
|
+
- 📊 **Multiple Formats**: LaTeX, MathML, AsciiMath, Markdown, SMILES
|
14
|
+
- 🧪 **BDD Tested**: 15+ Cucumber feature files with comprehensive coverage
|
15
|
+
- 🔌 **MCP Integration**: Full Model Context Protocol server support
|
16
|
+
- 🎲 **Balanced Ternary**: Seed 1069 encoding utilities
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add to your Gemfile:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem 'mathpix'
|
24
|
+
```
|
25
|
+
|
26
|
+
Or install directly:
|
27
|
+
|
28
|
+
```bash
|
29
|
+
gem install mathpix
|
30
|
+
```
|
31
|
+
|
32
|
+
## Quick Start
|
33
|
+
|
34
|
+
### Configuration
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
require 'mathpix'
|
38
|
+
|
39
|
+
Mathpix.configure do |config|
|
40
|
+
config.app_id = ENV['MATHPIX_APP_ID']
|
41
|
+
config.app_key = ENV['MATHPIX_APP_KEY']
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
Or use `.env` file:
|
46
|
+
|
47
|
+
```env
|
48
|
+
MATHPIX_APP_ID=your_app_id
|
49
|
+
MATHPIX_APP_KEY=your_app_key
|
50
|
+
```
|
51
|
+
|
52
|
+
### Basic Usage
|
53
|
+
|
54
|
+
#### Image to LaTeX
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
# Single image conversion
|
58
|
+
result = Mathpix.from('equation.png')
|
59
|
+
.with_formats(:latex_styled, :text)
|
60
|
+
.capture
|
61
|
+
|
62
|
+
puts result.latex # => "3x^{2} + 5x - 7 = 0"
|
63
|
+
puts result.confidence # => 0.99
|
64
|
+
```
|
65
|
+
|
66
|
+
#### URL to LaTeX
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
# Convert from URL
|
70
|
+
result = Mathpix.from_url('https://example.com/math.png')
|
71
|
+
.with_formats(:latex_styled, :mathml)
|
72
|
+
.capture
|
73
|
+
|
74
|
+
puts result.latex
|
75
|
+
puts result.mathml
|
76
|
+
```
|
77
|
+
|
78
|
+
#### Chemistry (SMILES)
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
# Chemistry structure recognition
|
82
|
+
result = Mathpix.from('molecule.png')
|
83
|
+
.with_formats(:smiles)
|
84
|
+
.capture
|
85
|
+
|
86
|
+
puts result.smiles # => "CC(C)CCO"
|
87
|
+
```
|
88
|
+
|
89
|
+
### PDF Conversion
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
# Async PDF to Markdown
|
93
|
+
pdf_job = Mathpix.pdf('research_paper.pdf')
|
94
|
+
.to_markdown
|
95
|
+
.async
|
96
|
+
.start
|
97
|
+
|
98
|
+
# Poll for completion
|
99
|
+
until pdf_job.complete?
|
100
|
+
sleep 2
|
101
|
+
pdf_job.refresh
|
102
|
+
end
|
103
|
+
|
104
|
+
puts pdf_job.markdown
|
105
|
+
```
|
106
|
+
|
107
|
+
### Batch Processing
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
images = Dir['equations/*.png']
|
111
|
+
|
112
|
+
results = Mathpix.batch(images) do |batch|
|
113
|
+
batch.formats :latex_styled, :text
|
114
|
+
batch.confidence_threshold 0.85
|
115
|
+
batch.max_threads 5
|
116
|
+
|
117
|
+
batch.on_each do |result|
|
118
|
+
puts "✓ #{result.latex}"
|
119
|
+
end
|
120
|
+
|
121
|
+
batch.on_error do |error, path|
|
122
|
+
puts "✗ Failed: #{path} - #{error.message}"
|
123
|
+
end
|
124
|
+
end.process
|
125
|
+
|
126
|
+
puts "Success rate: #{results.success_rate}"
|
127
|
+
puts "High confidence: #{results.confident(0.9).count}"
|
128
|
+
```
|
129
|
+
|
130
|
+
## Error Handling
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
begin
|
134
|
+
result = Mathpix.from('image.png').capture
|
135
|
+
rescue Mathpix::AuthenticationError => e
|
136
|
+
puts "Invalid credentials: #{e.message}"
|
137
|
+
rescue Mathpix::ValidationError => e
|
138
|
+
puts "Invalid input: #{e.message}"
|
139
|
+
rescue Mathpix::RateLimitError => e
|
140
|
+
puts "Rate limit exceeded. Retry after: #{e.retry_after}"
|
141
|
+
rescue Mathpix::SecurityError => e
|
142
|
+
puts "Security violation: #{e.message}"
|
143
|
+
rescue Mathpix::APIError => e
|
144
|
+
puts "API error: #{e.message}"
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
148
|
+
## Documentation
|
149
|
+
|
150
|
+
- [API Reference](https://docs.mathpix.com)
|
151
|
+
- [Changelog](CHANGELOG.md)
|
152
|
+
- [Security Policy](SECURITY.md)
|
153
|
+
|
154
|
+
## License
|
155
|
+
|
156
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
157
|
+
|
158
|
+
## Credits
|
159
|
+
|
160
|
+
- Developed by [TegLon Labs](https://github.com/teglonlabs)
|
161
|
+
- Mathpix API by [Mathpix](https://mathpix.com)
|
162
|
+
- Balanced ternary seed: 1069
|
163
|
+
|
164
|
+
## Support
|
165
|
+
|
166
|
+
- GitHub Issues: https://github.com/teglonlabs/mathpix-mcp-server/issues
|
167
|
+
- Email: ies@prototypesf.org
|
168
|
+
|
169
|
+
---
|
170
|
+
|
171
|
+
**The geodesic path to mathematical OCR in Ruby** ∎
|
data/SECURITY.md
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
# Security Policy
|
2
|
+
|
3
|
+
## Supported Versions
|
4
|
+
|
5
|
+
| Version | Supported |
|
6
|
+
| ------- | ------------------ |
|
7
|
+
| 0.1.x | :white_check_mark: |
|
8
|
+
|
9
|
+
## Reporting a Vulnerability
|
10
|
+
|
11
|
+
We take security vulnerabilities seriously. If you discover a security issue, please report it by emailing **ies@prototypesf.org** with the subject line "Mathpix Gem Security Vulnerability".
|
12
|
+
|
13
|
+
### What to Include
|
14
|
+
|
15
|
+
Please include the following information in your report:
|
16
|
+
|
17
|
+
- Description of the vulnerability
|
18
|
+
- Steps to reproduce the issue
|
19
|
+
- Potential impact
|
20
|
+
- Suggested fix (if available)
|
21
|
+
|
22
|
+
### Response Timeline
|
23
|
+
|
24
|
+
- **Initial Response**: Within 48 hours of report
|
25
|
+
- **Status Update**: Within 7 days with assessment
|
26
|
+
- **Fix Timeline**: Critical issues within 14 days, others within 30 days
|
27
|
+
- **Disclosure**: Coordinated disclosure after fix is available
|
28
|
+
|
29
|
+
## Security Features
|
30
|
+
|
31
|
+
This gem implements several security measures:
|
32
|
+
|
33
|
+
### HTTPS Enforcement
|
34
|
+
- All API requests forced to HTTPS
|
35
|
+
- HTTP URLs automatically upgraded
|
36
|
+
- Private IP addresses blocked
|
37
|
+
|
38
|
+
### Path Traversal Protection
|
39
|
+
- File paths expanded and validated
|
40
|
+
- Maximum path length enforced (4096 characters)
|
41
|
+
- Directory traversal attempts rejected
|
42
|
+
|
43
|
+
### File Size Limits
|
44
|
+
- Default maximum: 10MB per file
|
45
|
+
- Configurable via `Mathpix::Configuration`
|
46
|
+
- Prevents resource exhaustion attacks
|
47
|
+
|
48
|
+
### Rate Limiting
|
49
|
+
- Default: 60 requests per minute
|
50
|
+
- Burst capacity: 10 requests
|
51
|
+
- Configurable per-client
|
52
|
+
|
53
|
+
### Input Validation
|
54
|
+
- URL format validation
|
55
|
+
- File type verification
|
56
|
+
- Confidence threshold validation
|
57
|
+
|
58
|
+
## Known Security Considerations
|
59
|
+
|
60
|
+
### API Credentials
|
61
|
+
- Store credentials in environment variables or secure vaults
|
62
|
+
- Never commit credentials to version control
|
63
|
+
- Use `.env` files with `.gitignore` protection
|
64
|
+
|
65
|
+
### File Operations
|
66
|
+
- Be cautious with user-supplied file paths
|
67
|
+
- Validate file types before processing
|
68
|
+
- Monitor disk space usage for large batches
|
69
|
+
|
70
|
+
### Network Security
|
71
|
+
- API requests go to `api.mathpix.com` (official endpoint)
|
72
|
+
- TLS 1.2+ required for all connections
|
73
|
+
- Certificate validation enforced
|
74
|
+
|
75
|
+
## Security Best Practices
|
76
|
+
|
77
|
+
### Configuration
|
78
|
+
```ruby
|
79
|
+
Mathpix.configure do |config|
|
80
|
+
config.app_id = ENV['MATHPIX_APP_ID']
|
81
|
+
config.app_key = ENV['MATHPIX_APP_KEY']
|
82
|
+
config.max_file_size_mb = 5 # Restrict file size
|
83
|
+
config.https_only = true # Enforce HTTPS
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
### Batch Processing
|
88
|
+
```ruby
|
89
|
+
# Limit concurrent threads
|
90
|
+
Mathpix.batch(files, max_threads: 3) do |batch|
|
91
|
+
batch.on_error { |err, path| log_security_event(err, path) }
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
### Error Handling
|
96
|
+
```ruby
|
97
|
+
begin
|
98
|
+
result = Mathpix.from(user_path).capture
|
99
|
+
rescue Mathpix::SecurityError => e
|
100
|
+
# Handle security violations
|
101
|
+
logger.warn("Security violation: #{e.message}")
|
102
|
+
rescue Mathpix::ValidationError => e
|
103
|
+
# Handle validation failures
|
104
|
+
logger.error("Validation failed: #{e.message}")
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
## Dependency Security
|
109
|
+
|
110
|
+
We use the following tools to maintain dependency security:
|
111
|
+
|
112
|
+
- **bundler-audit**: Scans for known vulnerabilities
|
113
|
+
- **brakeman**: Static analysis for security issues
|
114
|
+
- **Dependabot**: Automated dependency updates
|
115
|
+
|
116
|
+
Run security scan:
|
117
|
+
```bash
|
118
|
+
bundle audit check --update
|
119
|
+
```
|
120
|
+
|
121
|
+
## MFA Requirement
|
122
|
+
|
123
|
+
This gem requires Multi-Factor Authentication (MFA) for RubyGems publishing as indicated in the gemspec:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
127
|
+
```
|
128
|
+
|
129
|
+
## Contact
|
130
|
+
|
131
|
+
For security-related questions or concerns:
|
132
|
+
- Email: ies@prototypesf.org
|
133
|
+
- GitHub Issues: https://github.com/teglonlabs/mathpix-mcp-server/issues (for non-sensitive issues)
|
134
|
+
|
135
|
+
## Acknowledgments
|
136
|
+
|
137
|
+
We appreciate responsible disclosure and will acknowledge security researchers who report vulnerabilities (with permission).
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mathpix
|
4
|
+
# Balanced Ternary support for Seed 1069
|
5
|
+
# The geodesic path: mathematical precision, test determinism
|
6
|
+
module BalancedTernary
|
7
|
+
# Seed 1069 balanced ternary pattern
|
8
|
+
# Mathematical verification:
|
9
|
+
# 1×3⁶ - 1×3⁵ - 1×3⁴ + 1×3³ + 1×3² + 1×3¹ + 1×3⁰
|
10
|
+
# = 729 - 243 - 81 + 27 + 9 + 3 + 1
|
11
|
+
# = 1069 ✓
|
12
|
+
SEED_1069_PATTERN = [+1, -1, -1, +1, +1, +1, +1].freeze
|
13
|
+
|
14
|
+
class << self
|
15
|
+
# Get the pattern for seed 1069
|
16
|
+
# @return [Array<Integer>]
|
17
|
+
def pattern
|
18
|
+
SEED_1069_PATTERN
|
19
|
+
end
|
20
|
+
|
21
|
+
# Generate confidence sequence following balanced ternary pattern
|
22
|
+
#
|
23
|
+
# @param base [Float] base confidence value
|
24
|
+
# @param delta [Float] variation per trit
|
25
|
+
# @return [Array<Float>] confidence values
|
26
|
+
# @example
|
27
|
+
# Mathpix::BalancedTernary.confidence_sequence(base: 0.92, delta: 0.02)
|
28
|
+
# # => [0.94, 0.90, 0.90, 0.94, 0.94, 0.94, 0.94]
|
29
|
+
def confidence_sequence(base: 0.92, delta: 0.02)
|
30
|
+
pattern.map { |trit| base + (trit * delta) }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Generate values with balanced ternary pattern
|
34
|
+
#
|
35
|
+
# @param count [Integer] number of values to generate
|
36
|
+
# @param seed [Integer] random seed
|
37
|
+
# @yield [trit, index] block receives trit value and index
|
38
|
+
# @return [Array]
|
39
|
+
# @example
|
40
|
+
# Mathpix::BalancedTernary.generate_with_pattern(10, seed: 1069) do |trit, i|
|
41
|
+
# puts "Position #{i}: trit=#{trit}"
|
42
|
+
# generate_test_data(trit)
|
43
|
+
# end
|
44
|
+
def generate_with_pattern(count, seed: 1069)
|
45
|
+
srand(seed)
|
46
|
+
Array.new(count) do |i|
|
47
|
+
trit = pattern[i % pattern.length]
|
48
|
+
yield(trit, i) if block_given?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Convert balanced ternary to decimal
|
53
|
+
#
|
54
|
+
# @param trits [Array<Integer>] balanced ternary digits
|
55
|
+
# @return [Integer] decimal value
|
56
|
+
# @example
|
57
|
+
# Mathpix::BalancedTernary.to_decimal([+1, -1, -1, +1, +1, +1, +1])
|
58
|
+
# # => 1069
|
59
|
+
def to_decimal(trits)
|
60
|
+
trits.reverse.each_with_index.sum { |trit, i| trit * (3**i) }
|
61
|
+
end
|
62
|
+
|
63
|
+
# Verify seed 1069 pattern
|
64
|
+
#
|
65
|
+
# @return [Boolean] true if pattern converts to 1069
|
66
|
+
def verify_seed_1069
|
67
|
+
to_decimal(pattern) == 1069
|
68
|
+
end
|
69
|
+
|
70
|
+
# Pattern interpretation
|
71
|
+
#
|
72
|
+
# @return [Hash] semantic meaning of pattern
|
73
|
+
def pattern_semantics
|
74
|
+
{
|
75
|
+
position_0: { trit: +1, meaning: 'Positive start (high confidence)' },
|
76
|
+
position_1: { trit: -1, meaning: 'Descent begins (complexity)' },
|
77
|
+
position_2: { trit: -1, meaning: 'Continued descent (adjustment)' },
|
78
|
+
position_3: { trit: +1, meaning: 'Recovery point (stabilization)' },
|
79
|
+
position_4: { trit: +1, meaning: 'Convergence (mastery)' },
|
80
|
+
position_5: { trit: +1, meaning: 'Stable convergence' },
|
81
|
+
position_6: { trit: +1, meaning: 'Final stability' }
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mathpix
|
4
|
+
# Batch processing builder
|
5
|
+
# The geodesic path: efficient, parallel, callback-driven
|
6
|
+
class Batch
|
7
|
+
attr_reader :client, :image_paths, :options, :callbacks
|
8
|
+
|
9
|
+
def initialize(client, image_paths)
|
10
|
+
@client = client
|
11
|
+
@image_paths = Array(image_paths)
|
12
|
+
@options = {}
|
13
|
+
@callbacks = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
# Set output formats
|
17
|
+
# @param formats [Array<Symbol>] format names
|
18
|
+
# @return [self]
|
19
|
+
def formats(*formats)
|
20
|
+
@options[:formats] = formats.flatten
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
# Set confidence threshold
|
25
|
+
# @param threshold [Float] minimum confidence
|
26
|
+
# @return [self]
|
27
|
+
def confidence_threshold(threshold)
|
28
|
+
@options[:confidence_threshold] = threshold
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
# Enable chemistry mode
|
33
|
+
# @return [self]
|
34
|
+
def chemistry_mode
|
35
|
+
@options[:chemistry] = true
|
36
|
+
@options[:include_smiles] = true
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
# Set preprocessing options
|
41
|
+
# @param options [Hash] preprocessing settings
|
42
|
+
# @return [self]
|
43
|
+
def preprocessing(**options)
|
44
|
+
@options[:preprocessing] = options
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
# Process asynchronously
|
49
|
+
# @return [self]
|
50
|
+
def async
|
51
|
+
@options[:async] = true
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
# Callback for each result
|
56
|
+
# @yield [Result] individual result
|
57
|
+
# @return [self]
|
58
|
+
def on_each(&block)
|
59
|
+
@callbacks[:each] = block
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
# Callback on completion
|
64
|
+
# @yield [Array<Result>] all results
|
65
|
+
# @return [self]
|
66
|
+
def on_complete(&block)
|
67
|
+
@callbacks[:complete] = block
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
# Callback on error
|
72
|
+
# @yield [Error, String] error and image path
|
73
|
+
# @return [self]
|
74
|
+
def on_error(&block)
|
75
|
+
@callbacks[:error] = block
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
# Execute batch processing
|
80
|
+
# @return [BatchResult]
|
81
|
+
def process
|
82
|
+
results = []
|
83
|
+
errors = []
|
84
|
+
|
85
|
+
image_paths.each do |path|
|
86
|
+
begin
|
87
|
+
result = client.snap(path, **options)
|
88
|
+
results << result
|
89
|
+
callbacks[:each]&.call(result)
|
90
|
+
rescue StandardError => e
|
91
|
+
errors << { path: path, error: e }
|
92
|
+
callbacks[:error]&.call(e, path)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
batch_result = BatchResult.new(results, errors)
|
97
|
+
callbacks[:complete]&.call(batch_result)
|
98
|
+
|
99
|
+
batch_result
|
100
|
+
end
|
101
|
+
|
102
|
+
alias call process
|
103
|
+
alias run process
|
104
|
+
end
|
105
|
+
|
106
|
+
# Batch Result container
|
107
|
+
class BatchResult
|
108
|
+
attr_reader :results, :errors
|
109
|
+
|
110
|
+
def initialize(results, errors = [])
|
111
|
+
@results = results
|
112
|
+
@errors = errors
|
113
|
+
end
|
114
|
+
|
115
|
+
def total
|
116
|
+
results.length + errors.length
|
117
|
+
end
|
118
|
+
|
119
|
+
def successful
|
120
|
+
results.length
|
121
|
+
end
|
122
|
+
|
123
|
+
def failed
|
124
|
+
errors.length
|
125
|
+
end
|
126
|
+
|
127
|
+
def success_rate
|
128
|
+
return 1.0 if total.zero?
|
129
|
+
successful.to_f / total
|
130
|
+
end
|
131
|
+
|
132
|
+
# Iterate over successful results
|
133
|
+
# @yield [Result]
|
134
|
+
def each(&block)
|
135
|
+
results.each(&block)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Get all LaTeX
|
139
|
+
# @return [Array<String>]
|
140
|
+
def all_latex
|
141
|
+
results.map(&:latex).compact
|
142
|
+
end
|
143
|
+
|
144
|
+
# Get all with confidence above threshold
|
145
|
+
# @param threshold [Float]
|
146
|
+
# @return [Array<Result>]
|
147
|
+
def confident(threshold = 0.90)
|
148
|
+
results.select { |r| r.confidence >= threshold }
|
149
|
+
end
|
150
|
+
|
151
|
+
def inspect
|
152
|
+
"#<Mathpix::BatchResult total=#{total} successful=#{successful} failed=#{failed}>"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|