agentbill-sdk 1.0.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 +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
- data/.github/pull_request_template.md +38 -0
- data/.github/workflows/ci.yml +47 -0
- data/.gitignore +14 -0
- data/.rspec +4 -0
- data/.rubocop.yml +58 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +42 -0
- data/CONTRIBUTING.md +135 -0
- data/Gemfile +19 -0
- data/LICENSE +21 -0
- data/README.md +149 -0
- data/Rakefile +32 -0
- data/SECURITY.md +73 -0
- data/agentbill.gemspec +25 -0
- data/examples/anthropic_basic.rb +28 -0
- data/examples/openai_basic.rb +28 -0
- data/lib/agentbill/tracer.rb +122 -0
- data/lib/agentbill/version.rb +3 -0
- data/lib/agentbill.rb +125 -0
- metadata +95 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2a5d6d3184f1e30d721acd50e8b51ac947d086352bdd4f3f26702adc127b021c
|
|
4
|
+
data.tar.gz: 57eb001ae9a6ba61c002855ad1abbe17fe14fcc3c8fc269b55903b780da669d2
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 686aef33e7ff8e70994ffe7ad034beddea13b5adf6fc9cd072288898c253f64c49aae92a28c7068f3de4252304b5b6ed5a86698886a87353ce3912afabb870b2
|
|
7
|
+
data.tar.gz: 2a86b18d8ea9a824fc2de991ae25f7f83473eda13f9ffaa96e6e451d1cca0729fa6cc3d3331e6a292a94e31fb604643b651e23697bbeec96dde6de6cae2d3c1f
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Create a report to help us improve
|
|
4
|
+
title: '[BUG] '
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**Describe the bug**
|
|
10
|
+
A clear and concise description of what the bug is.
|
|
11
|
+
|
|
12
|
+
**To Reproduce**
|
|
13
|
+
Steps to reproduce the behavior:
|
|
14
|
+
1. Initialize client with '...'
|
|
15
|
+
2. Call method '...'
|
|
16
|
+
3. See error
|
|
17
|
+
|
|
18
|
+
**Expected behavior**
|
|
19
|
+
A clear and concise description of what you expected to happen.
|
|
20
|
+
|
|
21
|
+
**Code Example**
|
|
22
|
+
```ruby
|
|
23
|
+
# Your code that reproduces the issue
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Environment:**
|
|
27
|
+
- Ruby version: [e.g. 3.2.0]
|
|
28
|
+
- Gem version: [e.g. 1.0.0]
|
|
29
|
+
- OS: [e.g. macOS 13.0]
|
|
30
|
+
- AI Provider: [e.g. OpenAI, Anthropic]
|
|
31
|
+
|
|
32
|
+
**Additional context**
|
|
33
|
+
Add any other context about the problem here.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest an idea for this project
|
|
4
|
+
title: '[FEATURE] '
|
|
5
|
+
labels: enhancement
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**Is your feature request related to a problem? Please describe.**
|
|
10
|
+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
11
|
+
|
|
12
|
+
**Describe the solution you'd like**
|
|
13
|
+
A clear and concise description of what you want to happen.
|
|
14
|
+
|
|
15
|
+
**Describe alternatives you've considered**
|
|
16
|
+
A clear and concise description of any alternative solutions or features you've considered.
|
|
17
|
+
|
|
18
|
+
**Use Case**
|
|
19
|
+
Describe how this feature would be used:
|
|
20
|
+
```ruby
|
|
21
|
+
# Example usage of the proposed feature
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Additional context**
|
|
25
|
+
Add any other context or screenshots about the feature request here.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Pull Request
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
<!-- Provide a detailed description of your changes -->
|
|
5
|
+
|
|
6
|
+
## Type of Change
|
|
7
|
+
<!-- Mark the relevant option with an "x" -->
|
|
8
|
+
|
|
9
|
+
- [ ] Bug fix (non-breaking change which fixes an issue)
|
|
10
|
+
- [ ] New feature (non-breaking change which adds functionality)
|
|
11
|
+
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
|
12
|
+
- [ ] Documentation update
|
|
13
|
+
- [ ] Code refactoring
|
|
14
|
+
- [ ] Performance improvement
|
|
15
|
+
|
|
16
|
+
## Testing
|
|
17
|
+
<!-- Describe the tests you ran to verify your changes -->
|
|
18
|
+
|
|
19
|
+
- [ ] All existing tests pass
|
|
20
|
+
- [ ] New tests added for new functionality
|
|
21
|
+
- [ ] Manual testing completed
|
|
22
|
+
|
|
23
|
+
## Checklist
|
|
24
|
+
<!-- Mark completed items with an "x" -->
|
|
25
|
+
|
|
26
|
+
- [ ] My code follows the style guidelines of this project (RuboCop passes)
|
|
27
|
+
- [ ] I have performed a self-review of my own code
|
|
28
|
+
- [ ] I have commented my code, particularly in hard-to-understand areas
|
|
29
|
+
- [ ] I have made corresponding changes to the documentation
|
|
30
|
+
- [ ] My changes generate no new warnings
|
|
31
|
+
- [ ] I have added tests that prove my fix is effective or that my feature works
|
|
32
|
+
- [ ] New and existing unit tests pass locally with my changes
|
|
33
|
+
- [ ] Any dependent changes have been merged and published
|
|
34
|
+
|
|
35
|
+
## Related Issues
|
|
36
|
+
<!-- Link any related issues here using #issue-number -->
|
|
37
|
+
|
|
38
|
+
Fixes #
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main, develop ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main, develop ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Test on Ruby ${{ matrix.ruby-version }}
|
|
12
|
+
runs-on: ${{ matrix.os }}
|
|
13
|
+
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
ruby-version: ['3.0', '3.1', '3.2', '3.3']
|
|
17
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Set up Ruby
|
|
23
|
+
uses: ruby/setup-ruby@v1
|
|
24
|
+
with:
|
|
25
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
26
|
+
bundler-cache: true
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: bundle install
|
|
30
|
+
|
|
31
|
+
- name: Run RuboCop
|
|
32
|
+
run: bundle exec rubocop
|
|
33
|
+
|
|
34
|
+
- name: Run tests
|
|
35
|
+
run: bundle exec rake spec
|
|
36
|
+
|
|
37
|
+
- name: Generate coverage
|
|
38
|
+
run: bundle exec rake coverage
|
|
39
|
+
|
|
40
|
+
- name: Build gem
|
|
41
|
+
run: gem build agentbill.gemspec
|
|
42
|
+
|
|
43
|
+
- name: Upload build artifacts
|
|
44
|
+
uses: actions/upload-artifact@v3
|
|
45
|
+
with:
|
|
46
|
+
name: gem-${{ matrix.ruby-version }}-${{ matrix.os }}
|
|
47
|
+
path: '*.gem'
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# RuboCop configuration for AgentBill Ruby SDK
|
|
2
|
+
|
|
3
|
+
require:
|
|
4
|
+
- rubocop-performance
|
|
5
|
+
- rubocop-rspec
|
|
6
|
+
|
|
7
|
+
AllCops:
|
|
8
|
+
TargetRubyVersion: 2.6
|
|
9
|
+
NewCops: enable
|
|
10
|
+
Exclude:
|
|
11
|
+
- 'vendor/**/*'
|
|
12
|
+
- 'spec/fixtures/**/*'
|
|
13
|
+
- 'tmp/**/*'
|
|
14
|
+
|
|
15
|
+
# Layout
|
|
16
|
+
Layout/LineLength:
|
|
17
|
+
Max: 120
|
|
18
|
+
AllowedPatterns: ['\A#']
|
|
19
|
+
|
|
20
|
+
Layout/MultilineMethodCallIndentation:
|
|
21
|
+
EnforcedStyle: indented
|
|
22
|
+
|
|
23
|
+
# Style
|
|
24
|
+
Style/Documentation:
|
|
25
|
+
Enabled: false
|
|
26
|
+
|
|
27
|
+
Style/StringLiterals:
|
|
28
|
+
EnforcedStyle: single_quotes
|
|
29
|
+
|
|
30
|
+
Style/FrozenStringLiteralComment:
|
|
31
|
+
Enabled: true
|
|
32
|
+
EnforcedStyle: always
|
|
33
|
+
|
|
34
|
+
# Metrics
|
|
35
|
+
Metrics/MethodLength:
|
|
36
|
+
Max: 30
|
|
37
|
+
Exclude:
|
|
38
|
+
- 'spec/**/*'
|
|
39
|
+
|
|
40
|
+
Metrics/BlockLength:
|
|
41
|
+
Max: 30
|
|
42
|
+
Exclude:
|
|
43
|
+
- 'spec/**/*'
|
|
44
|
+
- '*.gemspec'
|
|
45
|
+
|
|
46
|
+
Metrics/ClassLength:
|
|
47
|
+
Max: 150
|
|
48
|
+
|
|
49
|
+
Metrics/ModuleLength:
|
|
50
|
+
Max: 150
|
|
51
|
+
|
|
52
|
+
# Naming
|
|
53
|
+
Naming/FileName:
|
|
54
|
+
Enabled: true
|
|
55
|
+
|
|
56
|
+
# Performance
|
|
57
|
+
Performance/StringReplacement:
|
|
58
|
+
Enabled: true
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.2.0
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
## [1.0.0] - 2025-10-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial release of AgentBill Ruby SDK
|
|
12
|
+
- OpenAI client wrapping with automatic usage tracking
|
|
13
|
+
- Anthropic client wrapping with automatic usage tracking
|
|
14
|
+
- OpenTelemetry-based tracing infrastructure
|
|
15
|
+
- Custom signal tracking support
|
|
16
|
+
- Comprehensive test suite with RSpec
|
|
17
|
+
- GitHub Actions CI/CD workflows
|
|
18
|
+
- RuboCop code quality enforcement
|
|
19
|
+
- Professional documentation and examples
|
|
20
|
+
- MIT License
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
- Zero-config instrumentation for AI providers
|
|
24
|
+
- Automatic token and cost tracking
|
|
25
|
+
- Rich metadata capture
|
|
26
|
+
- Latency measurement
|
|
27
|
+
- Error tracking and reporting
|
|
28
|
+
- Customer-specific tracking support
|
|
29
|
+
- Debug logging capabilities
|
|
30
|
+
|
|
31
|
+
### Supported Providers
|
|
32
|
+
- OpenAI (GPT-4, GPT-3.5, etc.)
|
|
33
|
+
- Anthropic (Claude 3.5 Sonnet, etc.)
|
|
34
|
+
|
|
35
|
+
### Documentation
|
|
36
|
+
- Complete README with usage examples
|
|
37
|
+
- API documentation
|
|
38
|
+
- Contributing guidelines
|
|
39
|
+
- Security policy
|
|
40
|
+
- Code of conduct
|
|
41
|
+
|
|
42
|
+
[1.0.0]: https://github.com/Agent-Bill/Ruby/releases/tag/v1.0.0
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Contributing to AgentBill Ruby SDK
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing! This document provides guidelines for contributing to the AgentBill Ruby SDK.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
1. **Fork and clone the repository**
|
|
8
|
+
```bash
|
|
9
|
+
git clone https://github.com/YOUR_USERNAME/agentbill-ruby.git
|
|
10
|
+
cd agentbill-ruby
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
2. **Install dependencies**
|
|
14
|
+
```bash
|
|
15
|
+
bundle install
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
3. **Run tests**
|
|
19
|
+
```bash
|
|
20
|
+
bundle exec rake spec
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
4. **Run code quality checks**
|
|
24
|
+
```bash
|
|
25
|
+
bundle exec rubocop
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Development Workflow
|
|
29
|
+
|
|
30
|
+
1. **Create a feature branch**
|
|
31
|
+
```bash
|
|
32
|
+
git checkout -b feature/your-feature-name
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
2. **Make your changes**
|
|
36
|
+
- Write clean, readable code
|
|
37
|
+
- Follow Ruby style guidelines (RuboCop will check this)
|
|
38
|
+
- Add tests for new functionality
|
|
39
|
+
- Update documentation as needed
|
|
40
|
+
|
|
41
|
+
3. **Run quality checks**
|
|
42
|
+
```bash
|
|
43
|
+
bundle exec rake ci # Runs RuboCop + tests
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
4. **Commit your changes**
|
|
47
|
+
```bash
|
|
48
|
+
git add .
|
|
49
|
+
git commit -m "feat: Add your feature description"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
5. **Push and create pull request**
|
|
53
|
+
```bash
|
|
54
|
+
git push origin feature/your-feature-name
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Code Standards
|
|
58
|
+
|
|
59
|
+
### Style Guide
|
|
60
|
+
- Follow the [Ruby Style Guide](https://rubystyle.guide/)
|
|
61
|
+
- Use RuboCop to check code style: `bundle exec rubocop`
|
|
62
|
+
- Fix auto-fixable issues: `bundle exec rubocop -a`
|
|
63
|
+
|
|
64
|
+
### Testing
|
|
65
|
+
- Write RSpec tests for all new features
|
|
66
|
+
- Maintain minimum 90% code coverage
|
|
67
|
+
- Run tests: `bundle exec rake spec`
|
|
68
|
+
- Run with coverage: `bundle exec rake coverage`
|
|
69
|
+
|
|
70
|
+
### Documentation
|
|
71
|
+
- Add YARD documentation for public methods
|
|
72
|
+
- Update README.md for user-facing changes
|
|
73
|
+
- Add examples for new features
|
|
74
|
+
|
|
75
|
+
## Pull Request Process
|
|
76
|
+
|
|
77
|
+
1. **Ensure all checks pass**
|
|
78
|
+
- All tests pass
|
|
79
|
+
- RuboCop has no violations
|
|
80
|
+
- Code coverage is maintained
|
|
81
|
+
|
|
82
|
+
2. **Update documentation**
|
|
83
|
+
- Update README.md if needed
|
|
84
|
+
- Add CHANGELOG.md entry
|
|
85
|
+
- Add YARD documentation
|
|
86
|
+
|
|
87
|
+
3. **Fill out PR template**
|
|
88
|
+
- Describe your changes
|
|
89
|
+
- Link related issues
|
|
90
|
+
- Check all boxes in the template
|
|
91
|
+
|
|
92
|
+
4. **Wait for review**
|
|
93
|
+
- Address reviewer feedback
|
|
94
|
+
- Make requested changes
|
|
95
|
+
- Re-request review when ready
|
|
96
|
+
|
|
97
|
+
## Adding New AI Provider Support
|
|
98
|
+
|
|
99
|
+
To add support for a new AI provider:
|
|
100
|
+
|
|
101
|
+
1. **Add wrapper method in `lib/agentbill.rb`**
|
|
102
|
+
```ruby
|
|
103
|
+
def wrap_provider_name(client)
|
|
104
|
+
# Implementation
|
|
105
|
+
end
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
2. **Add tests in `spec/agentbill_spec.rb`**
|
|
109
|
+
```ruby
|
|
110
|
+
describe '#wrap_provider_name' do
|
|
111
|
+
# Tests
|
|
112
|
+
end
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
3. **Add example in `examples/`**
|
|
116
|
+
```ruby
|
|
117
|
+
# examples/provider_name_basic.rb
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
4. **Update README.md** with usage example
|
|
121
|
+
|
|
122
|
+
## Community Guidelines
|
|
123
|
+
|
|
124
|
+
- Be respectful and inclusive
|
|
125
|
+
- Help others learn and grow
|
|
126
|
+
- Provide constructive feedback
|
|
127
|
+
- Follow the [Code of Conduct](CODE_OF_CONDUCT.md)
|
|
128
|
+
|
|
129
|
+
## Getting Help
|
|
130
|
+
|
|
131
|
+
- Open an issue for bugs or feature requests
|
|
132
|
+
- Join discussions in pull requests
|
|
133
|
+
- Check existing issues and PRs first
|
|
134
|
+
|
|
135
|
+
Thank you for contributing! 🎉
|
data/Gemfile
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
|
|
5
|
+
# Specify your gem's dependencies in agentbill.gemspec
|
|
6
|
+
gemspec
|
|
7
|
+
|
|
8
|
+
group :development, :test do
|
|
9
|
+
gem 'rake', '~> 13.0'
|
|
10
|
+
gem 'rspec', '~> 3.9'
|
|
11
|
+
gem 'rubocop', '~> 1.21'
|
|
12
|
+
gem 'rubocop-performance', '~> 1.11'
|
|
13
|
+
gem 'rubocop-rspec', '~> 2.0'
|
|
14
|
+
gem 'simplecov', '~> 0.21'
|
|
15
|
+
gem 'webmock', '~> 3.14'
|
|
16
|
+
gem 'pry', '~> 0.14'
|
|
17
|
+
gem 'yard', '~> 0.9'
|
|
18
|
+
gem 'redcarpet', '~> 3.5'
|
|
19
|
+
end
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 AgentBill
|
|
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,149 @@
|
|
|
1
|
+
# AgentBill Ruby SDK
|
|
2
|
+
|
|
3
|
+
OpenTelemetry-based SDK for automatically tracking and billing AI agent usage.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### From GitHub (Recommended)
|
|
8
|
+
```ruby
|
|
9
|
+
# In your Gemfile
|
|
10
|
+
gem 'agentbill', git: 'https://github.com/Agent-Bill/Ruby.git'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### From RubyGems
|
|
14
|
+
```ruby
|
|
15
|
+
# In your Gemfile
|
|
16
|
+
gem 'agentbill'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then execute:
|
|
20
|
+
```bash
|
|
21
|
+
bundle install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### From Source
|
|
25
|
+
```bash
|
|
26
|
+
git clone https://github.com/Agent-Bill/Ruby.git
|
|
27
|
+
cd agentbill-ruby
|
|
28
|
+
gem build agentbill.gemspec
|
|
29
|
+
gem install agentbill-1.0.0.gem
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## File Structure
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
agentbill-ruby/
|
|
36
|
+
├── lib/
|
|
37
|
+
│ ├── agentbill.rb
|
|
38
|
+
│ └── agentbill/
|
|
39
|
+
│ ├── tracer.rb
|
|
40
|
+
│ └── version.rb
|
|
41
|
+
├── examples/
|
|
42
|
+
│ ├── openai_basic.rb
|
|
43
|
+
│ └── anthropic_basic.rb
|
|
44
|
+
├── spec/
|
|
45
|
+
├── README.md
|
|
46
|
+
├── agentbill.gemspec
|
|
47
|
+
├── Gemfile
|
|
48
|
+
├── Rakefile
|
|
49
|
+
└── LICENSE
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
### OpenAI
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
require 'agentbill'
|
|
58
|
+
require 'openai'
|
|
59
|
+
|
|
60
|
+
# Initialize AgentBill
|
|
61
|
+
agentbill = AgentBill::Client.init({
|
|
62
|
+
api_key: 'your-api-key',
|
|
63
|
+
customer_id: 'customer-123',
|
|
64
|
+
debug: true
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
# Wrap your OpenAI client
|
|
68
|
+
client = agentbill.wrap_openai(OpenAI::Client.new(
|
|
69
|
+
access_token: 'sk-...'
|
|
70
|
+
))
|
|
71
|
+
|
|
72
|
+
# Use normally - all calls are automatically tracked!
|
|
73
|
+
response = client.chat({
|
|
74
|
+
model: 'gpt-4',
|
|
75
|
+
messages: [{ role: 'user', content: 'Hello!' }]
|
|
76
|
+
})
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Anthropic
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
require 'agentbill'
|
|
83
|
+
require 'anthropic'
|
|
84
|
+
|
|
85
|
+
# Initialize AgentBill
|
|
86
|
+
agentbill = AgentBill::Client.init({
|
|
87
|
+
api_key: 'your-api-key',
|
|
88
|
+
customer_id: 'customer-123',
|
|
89
|
+
debug: true
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
# Wrap your Anthropic client
|
|
93
|
+
client = agentbill.wrap_anthropic(Anthropic::Client.new(
|
|
94
|
+
access_token: ENV['ANTHROPIC_API_KEY']
|
|
95
|
+
))
|
|
96
|
+
|
|
97
|
+
# Use normally - all calls are automatically tracked!
|
|
98
|
+
response = client.messages({
|
|
99
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
100
|
+
max_tokens: 1024,
|
|
101
|
+
messages: [{ role: 'user', content: 'Hello!' }]
|
|
102
|
+
})
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Features
|
|
106
|
+
|
|
107
|
+
- ✅ Zero-config instrumentation
|
|
108
|
+
- ✅ Accurate token & cost tracking
|
|
109
|
+
- ✅ Multi-provider support (OpenAI, Anthropic)
|
|
110
|
+
- ✅ Rich metadata capture
|
|
111
|
+
- ✅ OpenTelemetry-based
|
|
112
|
+
|
|
113
|
+
## Configuration
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
config = {
|
|
117
|
+
api_key: 'your-api-key', # Required
|
|
118
|
+
base_url: 'https://...', # Optional
|
|
119
|
+
customer_id: 'customer-123', # Optional
|
|
120
|
+
debug: true # Optional
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
agentbill = AgentBill::Client.init(config)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Publishing to RubyGems
|
|
127
|
+
|
|
128
|
+
### Prerequisites
|
|
129
|
+
1. Create a RubyGems account at https://rubygems.org/sign_up
|
|
130
|
+
2. Get your API key: `gem signin`
|
|
131
|
+
|
|
132
|
+
### Publishing Steps
|
|
133
|
+
```bash
|
|
134
|
+
# Build the gem
|
|
135
|
+
gem build agentbill.gemspec
|
|
136
|
+
|
|
137
|
+
# Push to RubyGems
|
|
138
|
+
gem push agentbill-1.0.0.gem
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## GitHub Repository Setup
|
|
142
|
+
|
|
143
|
+
1. Create repository: `agentbill-ruby`
|
|
144
|
+
2. Push all files from `lib/` directory
|
|
145
|
+
3. Tag releases: `git tag v1.0.0 && git push origin v1.0.0`
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
MIT
|
data/Rakefile
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/gem_tasks'
|
|
4
|
+
require 'rspec/core/rake_task'
|
|
5
|
+
require 'rubocop/rake_task'
|
|
6
|
+
|
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
8
|
+
RuboCop::RakeTask.new
|
|
9
|
+
|
|
10
|
+
desc 'Run all quality checks (RuboCop + tests)'
|
|
11
|
+
task ci: [:rubocop, :spec]
|
|
12
|
+
|
|
13
|
+
desc 'Generate code coverage report'
|
|
14
|
+
task :coverage do
|
|
15
|
+
ENV['COVERAGE'] = 'true'
|
|
16
|
+
Rake::Task['spec'].execute
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
desc 'Open development console'
|
|
20
|
+
task :console do
|
|
21
|
+
require 'irb'
|
|
22
|
+
require 'agentbill'
|
|
23
|
+
ARGV.clear
|
|
24
|
+
IRB.start
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
desc 'Generate YARD documentation'
|
|
28
|
+
task :doc do
|
|
29
|
+
system('yard doc')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
task default: :ci
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
We release security updates for the following versions:
|
|
6
|
+
|
|
7
|
+
| Version | Supported |
|
|
8
|
+
| ------- | ------------------ |
|
|
9
|
+
| 1.x.x | :white_check_mark: |
|
|
10
|
+
|
|
11
|
+
## Reporting a Vulnerability
|
|
12
|
+
|
|
13
|
+
We take security vulnerabilities seriously. If you discover a security issue, please follow responsible disclosure practices:
|
|
14
|
+
|
|
15
|
+
### How to Report
|
|
16
|
+
|
|
17
|
+
**DO NOT** create a public GitHub issue for security vulnerabilities.
|
|
18
|
+
|
|
19
|
+
Instead, please email: security@agentbill.com
|
|
20
|
+
|
|
21
|
+
Include:
|
|
22
|
+
- Description of the vulnerability
|
|
23
|
+
- Steps to reproduce
|
|
24
|
+
- Potential impact
|
|
25
|
+
- Suggested fix (if any)
|
|
26
|
+
|
|
27
|
+
### What to Expect
|
|
28
|
+
|
|
29
|
+
1. **Acknowledgment**: We'll acknowledge your report within 48 hours
|
|
30
|
+
2. **Investigation**: We'll investigate and keep you updated on progress
|
|
31
|
+
3. **Fix**: We'll develop and test a fix
|
|
32
|
+
4. **Release**: We'll release a security patch
|
|
33
|
+
5. **Credit**: We'll credit you in the security advisory (unless you prefer to remain anonymous)
|
|
34
|
+
|
|
35
|
+
### Security Best Practices
|
|
36
|
+
|
|
37
|
+
When using the AgentBill Ruby SDK:
|
|
38
|
+
|
|
39
|
+
1. **API Keys**
|
|
40
|
+
- Never commit API keys to version control
|
|
41
|
+
- Use environment variables: `ENV['AGENTBILL_API_KEY']`
|
|
42
|
+
- Rotate keys regularly
|
|
43
|
+
- Use different keys for development/production
|
|
44
|
+
|
|
45
|
+
2. **Dependencies**
|
|
46
|
+
- Keep the gem updated to the latest version
|
|
47
|
+
- Run `bundle update agentbill` regularly
|
|
48
|
+
- Monitor security advisories
|
|
49
|
+
|
|
50
|
+
3. **Data Privacy**
|
|
51
|
+
- Review what data is being tracked
|
|
52
|
+
- Implement proper data retention policies
|
|
53
|
+
- Follow GDPR/CCPA guidelines if applicable
|
|
54
|
+
|
|
55
|
+
4. **Network Security**
|
|
56
|
+
- Use HTTPS endpoints only
|
|
57
|
+
- Verify SSL certificates
|
|
58
|
+
- Use secure base URLs
|
|
59
|
+
|
|
60
|
+
## Security Features
|
|
61
|
+
|
|
62
|
+
The AgentBill SDK includes:
|
|
63
|
+
|
|
64
|
+
- ✅ HTTPS-only communication
|
|
65
|
+
- ✅ API key authentication
|
|
66
|
+
- ✅ No sensitive data logged by default
|
|
67
|
+
- ✅ Configurable debug mode for development
|
|
68
|
+
|
|
69
|
+
## Questions?
|
|
70
|
+
|
|
71
|
+
For general security questions, email: security@agentbill.com
|
|
72
|
+
|
|
73
|
+
Thank you for helping keep AgentBill secure! 🔒
|
data/agentbill.gemspec
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require_relative 'lib/agentbill/version'
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |spec|
|
|
4
|
+
spec.name = "agentbill-sdk"
|
|
5
|
+
spec.version = AgentBill::VERSION
|
|
6
|
+
spec.authors = ["AgentBill"]
|
|
7
|
+
spec.email = ["support@agentbill.com"]
|
|
8
|
+
|
|
9
|
+
spec.summary = "OpenTelemetry-based SDK for tracking AI agent usage and billing"
|
|
10
|
+
spec.description = "Automatically track and bill AI agent usage with zero-config instrumentation for OpenAI, Anthropic, and more"
|
|
11
|
+
spec.homepage = "https://github.com/Agent-Bill/Ruby"
|
|
12
|
+
spec.license = "MIT"
|
|
13
|
+
spec.required_ruby_version = ">= 2.7.0"
|
|
14
|
+
|
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
16
|
+
spec.metadata["source_code_uri"] = "https://github.com/Agent-Bill/Ruby"
|
|
17
|
+
|
|
18
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
19
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
|
20
|
+
end
|
|
21
|
+
spec.require_paths = ["lib"]
|
|
22
|
+
|
|
23
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
|
24
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
25
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'agentbill'
|
|
2
|
+
require 'anthropic'
|
|
3
|
+
|
|
4
|
+
# Initialize AgentBill with your API key
|
|
5
|
+
agentbill = AgentBill::Client.init({
|
|
6
|
+
api_key: ENV['AGENTBILL_API_KEY'] || 'your-api-key',
|
|
7
|
+
base_url: ENV['AGENTBILL_BASE_URL'],
|
|
8
|
+
customer_id: 'customer-123',
|
|
9
|
+
debug: true
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
# Wrap your Anthropic client
|
|
13
|
+
client = agentbill.wrap_anthropic(Anthropic::Client.new(
|
|
14
|
+
access_token: ENV['ANTHROPIC_API_KEY']
|
|
15
|
+
))
|
|
16
|
+
|
|
17
|
+
# Use Anthropic normally - tracking is automatic!
|
|
18
|
+
response = client.messages({
|
|
19
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
20
|
+
max_tokens: 1024,
|
|
21
|
+
messages: [
|
|
22
|
+
{ role: 'user', content: 'What is the capital of France?' }
|
|
23
|
+
]
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
puts response.dig('content', 0, 'text')
|
|
27
|
+
|
|
28
|
+
# All usage (tokens, cost, latency) is automatically tracked to your AgentBill dashboard
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'agentbill'
|
|
2
|
+
require 'openai'
|
|
3
|
+
|
|
4
|
+
# Initialize AgentBill with your API key
|
|
5
|
+
agentbill = AgentBill::Client.init({
|
|
6
|
+
api_key: ENV['AGENTBILL_API_KEY'] || 'your-api-key',
|
|
7
|
+
base_url: ENV['AGENTBILL_BASE_URL'],
|
|
8
|
+
customer_id: 'customer-123',
|
|
9
|
+
debug: true
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
# Wrap your OpenAI client
|
|
13
|
+
client = agentbill.wrap_openai(OpenAI::Client.new(
|
|
14
|
+
access_token: ENV['OPENAI_API_KEY']
|
|
15
|
+
))
|
|
16
|
+
|
|
17
|
+
# Use OpenAI normally - tracking is automatic!
|
|
18
|
+
response = client.chat({
|
|
19
|
+
model: 'gpt-4o-mini',
|
|
20
|
+
messages: [
|
|
21
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
22
|
+
{ role: 'user', content: 'What is the capital of France?' }
|
|
23
|
+
]
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
puts response['choices'][0]['message']['content']
|
|
27
|
+
|
|
28
|
+
# All usage (tokens, cost, latency) is automatically tracked to your AgentBill dashboard
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module AgentBill
|
|
6
|
+
class Span
|
|
7
|
+
attr_accessor :name, :trace_id, :span_id, :attributes, :start_time, :end_time, :status
|
|
8
|
+
|
|
9
|
+
def initialize(name, trace_id, span_id, attributes)
|
|
10
|
+
@name = name
|
|
11
|
+
@trace_id = trace_id
|
|
12
|
+
@span_id = span_id
|
|
13
|
+
@attributes = attributes
|
|
14
|
+
@start_time = (Time.now.to_f * 1_000_000_000).to_i
|
|
15
|
+
@end_time = nil
|
|
16
|
+
@status = { code: 0 }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def set_attributes(attrs)
|
|
20
|
+
@attributes.merge!(attrs)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def set_status(code, message = '')
|
|
24
|
+
@status = { code: code, message: message }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def finish
|
|
28
|
+
@end_time = (Time.now.to_f * 1_000_000_000).to_i
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class Tracer
|
|
33
|
+
def initialize(config)
|
|
34
|
+
@config = config
|
|
35
|
+
@base_url = config[:base_url] || 'https://uenhjwdtnxtchlmqarjo.supabase.co'
|
|
36
|
+
@api_key = config[:api_key]
|
|
37
|
+
@customer_id = config[:customer_id]
|
|
38
|
+
@debug = config[:debug] || false
|
|
39
|
+
@spans = []
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def start_span(name, attributes)
|
|
43
|
+
trace_id = SecureRandom.hex(16)
|
|
44
|
+
span_id = SecureRandom.hex(8)
|
|
45
|
+
|
|
46
|
+
attributes['service.name'] = 'agentbill-ruby-sdk'
|
|
47
|
+
attributes['customer.id'] = @customer_id if @customer_id
|
|
48
|
+
|
|
49
|
+
span = Span.new(name, trace_id, span_id, attributes)
|
|
50
|
+
@spans << span
|
|
51
|
+
span
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def flush
|
|
55
|
+
return if @spans.empty?
|
|
56
|
+
|
|
57
|
+
payload = build_otlp_payload
|
|
58
|
+
uri = URI("#{@base_url}/functions/v1/otel-collector")
|
|
59
|
+
|
|
60
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
61
|
+
http.use_ssl = true
|
|
62
|
+
|
|
63
|
+
request = Net::HTTP::Post.new(uri.path)
|
|
64
|
+
request['Authorization'] = "Bearer #{@api_key}"
|
|
65
|
+
request['Content-Type'] = 'application/json'
|
|
66
|
+
request.body = payload.to_json
|
|
67
|
+
|
|
68
|
+
response = http.request(request)
|
|
69
|
+
|
|
70
|
+
puts "AgentBill flush: #{response.code}" if @debug
|
|
71
|
+
|
|
72
|
+
@spans.clear if response.code.to_i == 200
|
|
73
|
+
rescue => e
|
|
74
|
+
puts "AgentBill flush error: #{e.message}" if @debug
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def build_otlp_payload
|
|
80
|
+
{
|
|
81
|
+
resourceSpans: [{
|
|
82
|
+
resource: {
|
|
83
|
+
attributes: [
|
|
84
|
+
{ key: 'service.name', value: { stringValue: 'agentbill-ruby-sdk' } },
|
|
85
|
+
{ key: 'service.version', value: { stringValue: '1.0.0' } }
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
scopeSpans: [{
|
|
89
|
+
scope: { name: 'agentbill', version: '1.0.0' },
|
|
90
|
+
spans: @spans.map { |span| span_to_otlp(span) }
|
|
91
|
+
}]
|
|
92
|
+
}]
|
|
93
|
+
}
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def span_to_otlp(span)
|
|
97
|
+
{
|
|
98
|
+
traceId: span.trace_id,
|
|
99
|
+
spanId: span.span_id,
|
|
100
|
+
name: span.name,
|
|
101
|
+
kind: 1,
|
|
102
|
+
startTimeUnixNano: span.start_time.to_s,
|
|
103
|
+
endTimeUnixNano: (span.end_time || (Time.now.to_f * 1_000_000_000).to_i).to_s,
|
|
104
|
+
attributes: span.attributes.map { |k, v| { key: k, value: value_to_otlp(v) } },
|
|
105
|
+
status: span.status
|
|
106
|
+
}
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def value_to_otlp(value)
|
|
110
|
+
case value
|
|
111
|
+
when String
|
|
112
|
+
{ stringValue: value }
|
|
113
|
+
when Integer, Float
|
|
114
|
+
{ intValue: value.to_i }
|
|
115
|
+
when TrueClass, FalseClass
|
|
116
|
+
{ boolValue: value }
|
|
117
|
+
else
|
|
118
|
+
{ stringValue: value.to_s }
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
data/lib/agentbill.rb
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
require_relative 'agentbill/version'
|
|
5
|
+
require_relative 'agentbill/tracer'
|
|
6
|
+
|
|
7
|
+
module AgentBill
|
|
8
|
+
class Client
|
|
9
|
+
attr_reader :config, :tracer
|
|
10
|
+
|
|
11
|
+
def initialize(config)
|
|
12
|
+
@config = config
|
|
13
|
+
@tracer = Tracer.new(config)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.init(config)
|
|
17
|
+
new(config)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def wrap_openai(client)
|
|
21
|
+
original_method = client.method(:chat)
|
|
22
|
+
|
|
23
|
+
client.define_singleton_method(:chat) do |params|
|
|
24
|
+
start_time = Time.now
|
|
25
|
+
|
|
26
|
+
span = @tracer.start_span('openai.chat.completion', {
|
|
27
|
+
'model' => params[:model] || 'unknown',
|
|
28
|
+
'provider' => 'openai'
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
begin
|
|
32
|
+
response = original_method.call(params)
|
|
33
|
+
|
|
34
|
+
latency = ((Time.now - start_time) * 1000).round
|
|
35
|
+
span.set_attributes({
|
|
36
|
+
'response.prompt_tokens' => response.dig(:usage, :prompt_tokens),
|
|
37
|
+
'response.completion_tokens' => response.dig(:usage, :completion_tokens),
|
|
38
|
+
'response.total_tokens' => response.dig(:usage, :total_tokens),
|
|
39
|
+
'latency_ms' => latency
|
|
40
|
+
})
|
|
41
|
+
span.set_status(0)
|
|
42
|
+
|
|
43
|
+
response
|
|
44
|
+
rescue => e
|
|
45
|
+
span.set_status(1, e.message)
|
|
46
|
+
raise
|
|
47
|
+
ensure
|
|
48
|
+
span.finish
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
client
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def wrap_anthropic(client)
|
|
56
|
+
original_method = client.method(:messages)
|
|
57
|
+
|
|
58
|
+
client.define_singleton_method(:messages) do |params|
|
|
59
|
+
start_time = Time.now
|
|
60
|
+
|
|
61
|
+
span = @tracer.start_span('anthropic.message', {
|
|
62
|
+
'model' => params[:model] || 'unknown',
|
|
63
|
+
'provider' => 'anthropic'
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
begin
|
|
67
|
+
response = original_method.call(params)
|
|
68
|
+
|
|
69
|
+
latency = ((Time.now - start_time) * 1000).round
|
|
70
|
+
span.set_attributes({
|
|
71
|
+
'response.input_tokens' => response.dig(:usage, :input_tokens),
|
|
72
|
+
'response.output_tokens' => response.dig(:usage, :output_tokens),
|
|
73
|
+
'latency_ms' => latency
|
|
74
|
+
})
|
|
75
|
+
span.set_status(0)
|
|
76
|
+
|
|
77
|
+
response
|
|
78
|
+
rescue => e
|
|
79
|
+
span.set_status(1, e.message)
|
|
80
|
+
raise
|
|
81
|
+
ensure
|
|
82
|
+
span.finish
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
client
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def track_signal(event_name:, revenue: 0, data: {})
|
|
90
|
+
uri = URI("#{@config[:base_url] || 'https://bgwyprqxtdreuutzpbgw.supabase.co'}/functions/v1/record-signals")
|
|
91
|
+
|
|
92
|
+
payload = {
|
|
93
|
+
event_name: event_name,
|
|
94
|
+
revenue: revenue,
|
|
95
|
+
customer_id: @config[:customer_id],
|
|
96
|
+
timestamp: Time.now.to_i,
|
|
97
|
+
data: data
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
begin
|
|
101
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
102
|
+
http.use_ssl = true
|
|
103
|
+
|
|
104
|
+
request = Net::HTTP::Post.new(uri.path)
|
|
105
|
+
request['Authorization'] = "Bearer #{@config[:api_key]}"
|
|
106
|
+
request['Content-Type'] = 'application/json'
|
|
107
|
+
request.body = payload.to_json
|
|
108
|
+
|
|
109
|
+
response = http.request(request)
|
|
110
|
+
|
|
111
|
+
if @config[:debug]
|
|
112
|
+
puts "[AgentBill] Signal tracked: #{event_name}, revenue: $#{revenue}"
|
|
113
|
+
end
|
|
114
|
+
rescue => e
|
|
115
|
+
if @config[:debug]
|
|
116
|
+
puts "[AgentBill] Failed to track signal: #{e.message}"
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def flush
|
|
122
|
+
@tracer.flush
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: agentbill-sdk
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- AgentBill
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-10-24 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.0'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '13.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '13.0'
|
|
41
|
+
description: Automatically track and bill AI agent usage with zero-config instrumentation
|
|
42
|
+
for OpenAI, Anthropic, and more
|
|
43
|
+
email:
|
|
44
|
+
- support@agentbill.com
|
|
45
|
+
executables: []
|
|
46
|
+
extensions: []
|
|
47
|
+
extra_rdoc_files: []
|
|
48
|
+
files:
|
|
49
|
+
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
|
50
|
+
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
|
51
|
+
- ".github/pull_request_template.md"
|
|
52
|
+
- ".github/workflows/ci.yml"
|
|
53
|
+
- ".gitignore"
|
|
54
|
+
- ".rspec"
|
|
55
|
+
- ".rubocop.yml"
|
|
56
|
+
- ".ruby-version"
|
|
57
|
+
- CHANGELOG.md
|
|
58
|
+
- CONTRIBUTING.md
|
|
59
|
+
- Gemfile
|
|
60
|
+
- LICENSE
|
|
61
|
+
- README.md
|
|
62
|
+
- Rakefile
|
|
63
|
+
- SECURITY.md
|
|
64
|
+
- agentbill.gemspec
|
|
65
|
+
- examples/anthropic_basic.rb
|
|
66
|
+
- examples/openai_basic.rb
|
|
67
|
+
- lib/agentbill.rb
|
|
68
|
+
- lib/agentbill/tracer.rb
|
|
69
|
+
- lib/agentbill/version.rb
|
|
70
|
+
homepage: https://github.com/Agent-Bill/Ruby
|
|
71
|
+
licenses:
|
|
72
|
+
- MIT
|
|
73
|
+
metadata:
|
|
74
|
+
homepage_uri: https://github.com/Agent-Bill/Ruby
|
|
75
|
+
source_code_uri: https://github.com/Agent-Bill/Ruby
|
|
76
|
+
post_install_message:
|
|
77
|
+
rdoc_options: []
|
|
78
|
+
require_paths:
|
|
79
|
+
- lib
|
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
81
|
+
requirements:
|
|
82
|
+
- - ">="
|
|
83
|
+
- !ruby/object:Gem::Version
|
|
84
|
+
version: 2.7.0
|
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
requirements: []
|
|
91
|
+
rubygems_version: 3.4.19
|
|
92
|
+
signing_key:
|
|
93
|
+
specification_version: 4
|
|
94
|
+
summary: OpenTelemetry-based SDK for tracking AI agent usage and billing
|
|
95
|
+
test_files: []
|