tork-governance 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.
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tork
4
+ # Configuration class for Tork SDK
5
+ class Configuration
6
+ # @return [String] The API key for authentication
7
+ attr_accessor :api_key
8
+
9
+ # @return [String] The base URL for the Tork API
10
+ attr_accessor :base_url
11
+
12
+ # @return [Integer] Request timeout in seconds
13
+ attr_accessor :timeout
14
+
15
+ # @return [Integer] Maximum number of retries for failed requests
16
+ attr_accessor :max_retries
17
+
18
+ # @return [Float] Base delay for exponential backoff (in seconds)
19
+ attr_accessor :retry_base_delay
20
+
21
+ # @return [Float] Maximum delay between retries (in seconds)
22
+ attr_accessor :retry_max_delay
23
+
24
+ # @return [Boolean] Whether to raise on rate limit or return error response
25
+ attr_accessor :raise_on_rate_limit
26
+
27
+ # @return [Logger, nil] Logger instance for debugging
28
+ attr_accessor :logger
29
+
30
+ # @return [String, nil] Custom user agent string
31
+ attr_accessor :user_agent
32
+
33
+ # Default configuration values
34
+ DEFAULTS = {
35
+ base_url: "https://api.tork.network/v1",
36
+ timeout: 30,
37
+ max_retries: 3,
38
+ retry_base_delay: 0.5,
39
+ retry_max_delay: 30.0,
40
+ raise_on_rate_limit: true,
41
+ logger: nil,
42
+ user_agent: nil
43
+ }.freeze
44
+
45
+ def initialize
46
+ DEFAULTS.each do |key, value|
47
+ send("#{key}=", value)
48
+ end
49
+ @api_key = ENV["TORK_API_KEY"]
50
+ end
51
+
52
+ # Validate the configuration
53
+ # @raise [Tork::AuthenticationError] if API key is missing
54
+ def validate!
55
+ raise AuthenticationError, "API key is required" if api_key.nil? || api_key.empty?
56
+ end
57
+
58
+ # Reset configuration to defaults
59
+ def reset!
60
+ DEFAULTS.each do |key, value|
61
+ send("#{key}=", value)
62
+ end
63
+ @api_key = ENV["TORK_API_KEY"]
64
+ end
65
+
66
+ # Build user agent string
67
+ # @return [String]
68
+ def full_user_agent
69
+ base = "tork-ruby/#{Tork::VERSION} Ruby/#{RUBY_VERSION}"
70
+ user_agent ? "#{user_agent} #{base}" : base
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tork
4
+ # Base error class for all Tork errors
5
+ class Error < StandardError
6
+ attr_reader :http_status, :code, :details
7
+
8
+ def initialize(message = nil, http_status: nil, code: nil, details: nil)
9
+ @http_status = http_status
10
+ @code = code
11
+ @details = details
12
+ super(message)
13
+ end
14
+ end
15
+
16
+ # Raised when API authentication fails
17
+ class AuthenticationError < Error
18
+ def initialize(message = "Invalid or missing API key")
19
+ super(message, http_status: 401, code: "AUTHENTICATION_ERROR")
20
+ end
21
+ end
22
+
23
+ # Raised when rate limit is exceeded
24
+ class RateLimitError < Error
25
+ attr_reader :retry_after
26
+
27
+ def initialize(message = "Rate limit exceeded", retry_after: nil)
28
+ @retry_after = retry_after
29
+ super(message, http_status: 429, code: "RATE_LIMIT_ERROR")
30
+ end
31
+ end
32
+
33
+ # Raised when a resource is not found
34
+ class NotFoundError < Error
35
+ def initialize(message = "Resource not found")
36
+ super(message, http_status: 404, code: "NOT_FOUND")
37
+ end
38
+ end
39
+
40
+ # Raised when request validation fails
41
+ class ValidationError < Error
42
+ def initialize(message = "Validation failed", details: nil)
43
+ super(message, http_status: 400, code: "VALIDATION_ERROR", details: details)
44
+ end
45
+ end
46
+
47
+ # Raised when the server returns an error
48
+ class ServerError < Error
49
+ def initialize(message = "Server error")
50
+ super(message, http_status: 500, code: "SERVER_ERROR")
51
+ end
52
+ end
53
+
54
+ # Raised when a request times out
55
+ class TimeoutError < Error
56
+ def initialize(message = "Request timed out")
57
+ super(message, code: "TIMEOUT_ERROR")
58
+ end
59
+ end
60
+
61
+ # Raised when a connection cannot be established
62
+ class ConnectionError < Error
63
+ def initialize(message = "Failed to connect to Tork API")
64
+ super(message, code: "CONNECTION_ERROR")
65
+ end
66
+ end
67
+
68
+ # Raised when a policy violation is detected
69
+ class PolicyViolationError < Error
70
+ attr_reader :violations
71
+
72
+ def initialize(message = "Policy violation detected", violations: [])
73
+ @violations = violations
74
+ super(message, http_status: 422, code: "POLICY_VIOLATION")
75
+ end
76
+ end
77
+
78
+ # Raised when usage limit is exceeded
79
+ class UsageLimitError < Error
80
+ def initialize(message = "Usage limit exceeded")
81
+ super(message, http_status: 429, code: "USAGE_LIMIT_ERROR")
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tork
4
+ module Resources
5
+ # Evaluation resource for content evaluation
6
+ class Evaluation
7
+ # @param client [Tork::Client] The API client
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ # Evaluate content against policies
13
+ # @param prompt [String] The prompt/input to evaluate
14
+ # @param response [String, nil] The response/output to evaluate
15
+ # @param policy_id [String, nil] Specific policy ID (uses default if nil)
16
+ # @param checks [Array<String>] Checks to perform (pii, toxicity, moderation)
17
+ # @param options [Hash] Additional options
18
+ # @return [Hash] Evaluation result
19
+ def create(prompt:, response: nil, policy_id: nil, checks: nil, **options)
20
+ body = { content: prompt }
21
+ body[:response] = response if response
22
+ body[:policy_id] = policy_id if policy_id
23
+ body[:checks] = checks if checks
24
+ body.merge!(options) unless options.empty?
25
+
26
+ @client.post("/evaluate", body)
27
+ end
28
+
29
+ # Get an evaluation by ID
30
+ # @param id [String] Evaluation ID
31
+ # @return [Hash] Evaluation details
32
+ def get(id)
33
+ @client.get("/evaluations/#{id}")
34
+ end
35
+
36
+ # List recent evaluations
37
+ # @param page [Integer] Page number
38
+ # @param per_page [Integer] Items per page
39
+ # @param policy_id [String, nil] Filter by policy
40
+ # @param status [String, nil] Filter by status (passed, failed, flagged)
41
+ # @param start_date [String, nil] Start date (ISO 8601)
42
+ # @param end_date [String, nil] End date (ISO 8601)
43
+ # @return [Hash] List of evaluations
44
+ def list(page: 1, per_page: 20, policy_id: nil, status: nil, start_date: nil, end_date: nil)
45
+ params = { page: page, per_page: per_page }
46
+ params[:policy_id] = policy_id if policy_id
47
+ params[:status] = status if status
48
+ params[:start_date] = start_date if start_date
49
+ params[:end_date] = end_date if end_date
50
+ @client.get("/evaluations", params)
51
+ end
52
+
53
+ # Batch evaluate multiple content items
54
+ # @param items [Array<Hash>] Array of items to evaluate
55
+ # @param policy_id [String, nil] Policy to apply to all
56
+ # @return [Hash] Batch evaluation results
57
+ def batch(items, policy_id: nil)
58
+ body = { items: items }
59
+ body[:policy_id] = policy_id if policy_id
60
+ @client.post("/evaluate/batch", body)
61
+ end
62
+
63
+ # Evaluate and get detailed analysis
64
+ # @param prompt [String] Content to analyze
65
+ # @param analysis_types [Array<String>] Types of analysis
66
+ # @return [Hash] Detailed analysis
67
+ def analyze(prompt:, analysis_types: %w[pii toxicity sentiment topics])
68
+ @client.post("/evaluate/analyze", {
69
+ content: prompt,
70
+ analysis_types: analysis_types
71
+ })
72
+ end
73
+
74
+ # Check PII in content
75
+ # @param content [String] Content to check
76
+ # @param pii_types [Array<String>, nil] Specific PII types to detect
77
+ # @param redact [Boolean] Whether to return redacted version
78
+ # @return [Hash] PII detection results
79
+ def detect_pii(content:, pii_types: nil, redact: false)
80
+ body = { content: content, redact: redact }
81
+ body[:pii_types] = pii_types if pii_types
82
+ @client.post("/pii/detect", body)
83
+ end
84
+
85
+ # Redact PII from content
86
+ # @param content [String] Content to redact
87
+ # @param pii_types [Array<String>, nil] Specific PII types to redact
88
+ # @param replacement [String] Replacement format (mask, type, custom)
89
+ # @return [Hash] Redacted content
90
+ def redact_pii(content:, pii_types: nil, replacement: "mask")
91
+ body = { content: content, replacement: replacement }
92
+ body[:pii_types] = pii_types if pii_types
93
+ @client.post("/pii/redact", body)
94
+ end
95
+
96
+ # Check for jailbreak attempts
97
+ # @param prompt [String] Prompt to check
98
+ # @return [Hash] Jailbreak detection results
99
+ def detect_jailbreak(prompt:)
100
+ @client.post("/jailbreak/detect", { prompt: prompt })
101
+ end
102
+
103
+ # Validate RAG chunks
104
+ # @param chunks [Array<Hash>] RAG chunks to validate
105
+ # @param query [String] Original query
106
+ # @return [Hash] Validation results
107
+ def validate_rag(chunks:, query:)
108
+ @client.post("/rag/validate", {
109
+ chunks: chunks,
110
+ query: query
111
+ })
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tork
4
+ module Resources
5
+ # Metrics resource for analytics and reporting
6
+ class Metrics
7
+ # @param client [Tork::Client] The API client
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ # Get Torking X score for an evaluation
13
+ # @param evaluation_id [String] Evaluation ID
14
+ # @return [Hash] Torking X metrics
15
+ def torking_x(evaluation_id:)
16
+ @client.get("/metrics/torking-x/#{evaluation_id}")
17
+ end
18
+
19
+ # Get usage statistics
20
+ # @param period [String] Time period (day, week, month, year)
21
+ # @param start_date [String, nil] Start date (ISO 8601)
22
+ # @param end_date [String, nil] End date (ISO 8601)
23
+ # @return [Hash] Usage statistics
24
+ def usage(period: "month", start_date: nil, end_date: nil)
25
+ params = { period: period }
26
+ params[:start_date] = start_date if start_date
27
+ params[:end_date] = end_date if end_date
28
+ @client.get("/metrics/usage", params)
29
+ end
30
+
31
+ # Get policy performance metrics
32
+ # @param policy_id [String, nil] Specific policy ID
33
+ # @param period [String] Time period
34
+ # @return [Hash] Policy metrics
35
+ def policy_performance(policy_id: nil, period: "month")
36
+ params = { period: period }
37
+ params[:policy_id] = policy_id if policy_id
38
+ @client.get("/metrics/policies", params)
39
+ end
40
+
41
+ # Get violation statistics
42
+ # @param period [String] Time period
43
+ # @param group_by [String] Grouping (policy, type, severity)
44
+ # @return [Hash] Violation statistics
45
+ def violations(period: "month", group_by: "type")
46
+ @client.get("/metrics/violations", {
47
+ period: period,
48
+ group_by: group_by
49
+ })
50
+ end
51
+
52
+ # Get PII detection statistics
53
+ # @param period [String] Time period
54
+ # @return [Hash] PII statistics
55
+ def pii_stats(period: "month")
56
+ @client.get("/metrics/pii", { period: period })
57
+ end
58
+
59
+ # Get latency metrics
60
+ # @param period [String] Time period
61
+ # @param percentiles [Array<Integer>] Percentiles to calculate
62
+ # @return [Hash] Latency metrics
63
+ def latency(period: "day", percentiles: [50, 95, 99])
64
+ @client.get("/metrics/latency", {
65
+ period: period,
66
+ percentiles: percentiles.join(",")
67
+ })
68
+ end
69
+
70
+ # Get cost metrics
71
+ # @param period [String] Time period
72
+ # @param group_by [String] Grouping (day, policy, model)
73
+ # @return [Hash] Cost breakdown
74
+ def costs(period: "month", group_by: "day")
75
+ @client.get("/metrics/costs", {
76
+ period: period,
77
+ group_by: group_by
78
+ })
79
+ end
80
+
81
+ # Get real-time metrics
82
+ # @return [Hash] Current metrics snapshot
83
+ def realtime
84
+ @client.get("/metrics/realtime")
85
+ end
86
+
87
+ # Get compliance report
88
+ # @param start_date [String] Start date (ISO 8601)
89
+ # @param end_date [String] End date (ISO 8601)
90
+ # @param format [String] Report format (json, pdf)
91
+ # @return [Hash] Compliance report data
92
+ def compliance_report(start_date:, end_date:, format: "json")
93
+ @client.get("/metrics/compliance", {
94
+ start_date: start_date,
95
+ end_date: end_date,
96
+ format: format
97
+ })
98
+ end
99
+
100
+ # Get dashboard summary
101
+ # @return [Hash] Dashboard metrics
102
+ def dashboard
103
+ @client.get("/metrics/dashboard")
104
+ end
105
+
106
+ # Export metrics
107
+ # @param type [String] Export type (usage, violations, costs)
108
+ # @param start_date [String] Start date
109
+ # @param end_date [String] End date
110
+ # @param format [String] Export format (csv, json)
111
+ # @return [Hash] Export URL or data
112
+ def export(type:, start_date:, end_date:, format: "csv")
113
+ @client.post("/metrics/export", {
114
+ type: type,
115
+ start_date: start_date,
116
+ end_date: end_date,
117
+ format: format
118
+ })
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tork
4
+ module Resources
5
+ # Policy resource for managing governance policies
6
+ class Policy
7
+ # @param client [Tork::Client] The API client
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ # List all policies
13
+ # @param page [Integer] Page number (default: 1)
14
+ # @param per_page [Integer] Items per page (default: 20)
15
+ # @param status [String, nil] Filter by status (active, draft, archived)
16
+ # @return [Hash] List of policies with pagination info
17
+ def list(page: 1, per_page: 20, status: nil)
18
+ params = { page: page, per_page: per_page }
19
+ params[:status] = status if status
20
+ @client.get("/policies", params)
21
+ end
22
+
23
+ # Get a specific policy
24
+ # @param id [String] Policy ID
25
+ # @return [Hash] Policy details
26
+ def get(id)
27
+ @client.get("/policies/#{id}")
28
+ end
29
+
30
+ # Create a new policy
31
+ # @param name [String] Policy name
32
+ # @param description [String, nil] Policy description
33
+ # @param rules [Array<Hash>] Policy rules
34
+ # @param enabled [Boolean] Whether policy is enabled (default: true)
35
+ # @param options [Hash] Additional options
36
+ # @return [Hash] Created policy
37
+ def create(name:, rules:, description: nil, enabled: true, **options)
38
+ body = {
39
+ name: name,
40
+ rules: rules,
41
+ enabled: enabled,
42
+ **options
43
+ }
44
+ body[:description] = description if description
45
+ @client.post("/policies", body)
46
+ end
47
+
48
+ # Update a policy
49
+ # @param id [String] Policy ID
50
+ # @param attributes [Hash] Attributes to update
51
+ # @return [Hash] Updated policy
52
+ def update(id, **attributes)
53
+ @client.patch("/policies/#{id}", attributes)
54
+ end
55
+
56
+ # Delete a policy
57
+ # @param id [String] Policy ID
58
+ # @return [Hash] Deletion confirmation
59
+ def delete(id)
60
+ @client.delete("/policies/#{id}")
61
+ end
62
+
63
+ # Duplicate a policy
64
+ # @param id [String] Policy ID to duplicate
65
+ # @param name [String, nil] New name for the duplicate
66
+ # @return [Hash] Duplicated policy
67
+ def duplicate(id, name: nil)
68
+ body = {}
69
+ body[:name] = name if name
70
+ @client.post("/policies/#{id}/duplicate", body)
71
+ end
72
+
73
+ # Enable a policy
74
+ # @param id [String] Policy ID
75
+ # @return [Hash] Updated policy
76
+ def enable(id)
77
+ update(id, enabled: true)
78
+ end
79
+
80
+ # Disable a policy
81
+ # @param id [String] Policy ID
82
+ # @return [Hash] Updated policy
83
+ def disable(id)
84
+ update(id, enabled: false)
85
+ end
86
+
87
+ # Get policy versions
88
+ # @param id [String] Policy ID
89
+ # @return [Hash] List of versions
90
+ def versions(id)
91
+ @client.get("/policies/#{id}/versions")
92
+ end
93
+
94
+ # Restore a policy version
95
+ # @param id [String] Policy ID
96
+ # @param version_id [String] Version ID to restore
97
+ # @return [Hash] Restored policy
98
+ def restore_version(id, version_id)
99
+ @client.post("/policies/#{id}/versions/#{version_id}/restore")
100
+ end
101
+
102
+ # Test a policy with sample content
103
+ # @param id [String] Policy ID
104
+ # @param content [String] Content to test
105
+ # @param context [Hash] Additional context
106
+ # @return [Hash] Test results
107
+ def test(id, content:, context: {})
108
+ @client.post("/policies/#{id}/test", {
109
+ content: content,
110
+ context: context
111
+ })
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tork
4
+ VERSION = "0.1.0"
5
+ end
data/lib/tork.rb ADDED
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "tork/version"
4
+ require_relative "tork/errors"
5
+ require_relative "tork/configuration"
6
+ require_relative "tork/client"
7
+ require_relative "tork/resources/policy"
8
+ require_relative "tork/resources/evaluation"
9
+ require_relative "tork/resources/metrics"
10
+
11
+ # Tork AI Governance SDK for Ruby
12
+ #
13
+ # @example Basic usage
14
+ # Tork.configure do |config|
15
+ # config.api_key = "tork_your_api_key"
16
+ # end
17
+ #
18
+ # client = Tork::Client.new
19
+ # result = client.evaluate(prompt: "Hello world")
20
+ #
21
+ # @example Direct client initialization
22
+ # client = Tork::Client.new(api_key: "tork_your_api_key")
23
+ # result = client.evaluate(prompt: "Hello world")
24
+ #
25
+ module Tork
26
+ class << self
27
+ # @return [Configuration] Global configuration instance
28
+ def configuration
29
+ @configuration ||= Configuration.new
30
+ end
31
+
32
+ # Configure the Tork SDK
33
+ # @yield [Configuration] Configuration instance
34
+ # @example
35
+ # Tork.configure do |config|
36
+ # config.api_key = "tork_your_api_key"
37
+ # config.timeout = 60
38
+ # end
39
+ def configure
40
+ yield(configuration)
41
+ end
42
+
43
+ # Reset configuration to defaults
44
+ def reset_configuration!
45
+ @configuration = Configuration.new
46
+ end
47
+
48
+ # Create a new client with global configuration
49
+ # @return [Client]
50
+ def client
51
+ @client ||= Client.new
52
+ end
53
+
54
+ # Reset the default client
55
+ def reset_client!
56
+ @client = nil
57
+ end
58
+
59
+ # Convenience method to evaluate content
60
+ # @param prompt [String] Content to evaluate
61
+ # @param options [Hash] Additional options
62
+ # @return [Hash] Evaluation result
63
+ def evaluate(prompt:, **options)
64
+ client.evaluate(prompt: prompt, **options)
65
+ end
66
+
67
+ # Access policies via default client
68
+ # @return [Resources::Policy]
69
+ def policies
70
+ client.policies
71
+ end
72
+
73
+ # Access evaluations via default client
74
+ # @return [Resources::Evaluation]
75
+ def evaluations
76
+ client.evaluations
77
+ end
78
+
79
+ # Access metrics via default client
80
+ # @return [Resources::Metrics]
81
+ def metrics
82
+ client.metrics
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/tork/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "tork-governance"
7
+ spec.version = Tork::VERSION
8
+ spec.authors = ["Tork Network"]
9
+ spec.email = ["support@tork.network"]
10
+
11
+ spec.summary = "Ruby SDK for Tork AI Governance Platform"
12
+ spec.description = <<~DESC
13
+ Official Ruby SDK for the Tork AI Governance Platform. Provides comprehensive
14
+ tools for AI safety, content moderation, PII detection, policy enforcement,
15
+ and compliance monitoring for LLM applications.
16
+ DESC
17
+ spec.homepage = "https://tork.network"
18
+ spec.license = "MIT"
19
+ spec.required_ruby_version = ">= 2.7.0"
20
+
21
+ spec.metadata["homepage_uri"] = spec.homepage
22
+ spec.metadata["source_code_uri"] = "https://github.com/torkjacobs/tork-ruby-sdk"
23
+ spec.metadata["changelog_uri"] = "https://github.com/torkjacobs/tork-ruby-sdk/blob/main/CHANGELOG.md"
24
+ spec.metadata["documentation_uri"] = "https://docs.tork.network/sdks/ruby"
25
+ spec.metadata["bug_tracker_uri"] = "https://github.com/torkjacobs/tork-ruby-sdk/issues"
26
+ spec.metadata["rubygems_mfa_required"] = "true"
27
+
28
+ # Specify which files should be added to the gem
29
+ spec.files = Dir.chdir(__dir__) do
30
+ `git ls-files -z`.split("\x0").reject do |f|
31
+ (File.expand_path(f) == __FILE__) ||
32
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
33
+ end
34
+ end
35
+ spec.bindir = "exe"
36
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
37
+ spec.require_paths = ["lib"]
38
+
39
+ # Runtime dependencies
40
+ spec.add_dependency "faraday", "~> 2.0"
41
+ spec.add_dependency "faraday-retry", "~> 2.0"
42
+
43
+ # Development dependencies
44
+ spec.add_development_dependency "bundler", "~> 2.0"
45
+ spec.add_development_dependency "rake", "~> 13.0"
46
+ spec.add_development_dependency "rspec", "~> 3.0"
47
+ spec.add_development_dependency "rubocop", "~> 1.0"
48
+ spec.add_development_dependency "rubocop-rspec", "~> 2.0"
49
+ spec.add_development_dependency "simplecov", "~> 0.22"
50
+ spec.add_development_dependency "vcr", "~> 6.0"
51
+ spec.add_development_dependency "webmock", "~> 3.0"
52
+ spec.add_development_dependency "yard", "~> 0.9"
53
+ end