tavus 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,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "json"
5
+
6
+ module Tavus
7
+ class Client
8
+ attr_reader :configuration
9
+
10
+ def initialize(api_key: nil, base_url: nil, timeout: nil)
11
+ @configuration = Configuration.new
12
+ @configuration.api_key = api_key || Tavus.configuration.api_key
13
+ @configuration.base_url = base_url || Tavus.configuration.base_url
14
+ @configuration.timeout = timeout || Tavus.configuration.timeout
15
+
16
+ validate_configuration!
17
+ end
18
+
19
+ def conversations
20
+ @conversations ||= Resources::Conversations.new(self)
21
+ end
22
+
23
+ def personas
24
+ @personas ||= Resources::Personas.new(self)
25
+ end
26
+
27
+ def replicas
28
+ @replicas ||= Resources::Replicas.new(self)
29
+ end
30
+
31
+ def objectives
32
+ @objectives ||= Resources::Objectives.new(self)
33
+ end
34
+
35
+ def guardrails
36
+ @guardrails ||= Resources::Guardrails.new(self)
37
+ end
38
+
39
+ def documents
40
+ @documents ||= Resources::Documents.new(self)
41
+ end
42
+
43
+ def videos
44
+ @videos ||= Resources::Videos.new(self)
45
+ end
46
+
47
+ def get(path, params: {})
48
+ request(:get, path, params: params)
49
+ end
50
+
51
+ def post(path, body: {}, params: {})
52
+ request(:post, path, body: body, params: params)
53
+ end
54
+
55
+ def patch(path, body: {}, params: {})
56
+ request(:patch, path, body: body, params: params)
57
+ end
58
+
59
+ def delete(path, params: {})
60
+ request(:delete, path, params: params)
61
+ end
62
+
63
+ private
64
+
65
+ def validate_configuration!
66
+ raise ConfigurationError, "API key is required" unless configuration.valid?
67
+ end
68
+
69
+ def request(method, path, body: {}, params: {})
70
+ uri = build_uri(path, params)
71
+ http = build_http(uri)
72
+ request = build_request(method, uri, body)
73
+
74
+ response = http.request(request)
75
+ handle_response(response)
76
+ rescue StandardError => e
77
+ raise ApiError, "Request failed: #{e.message}"
78
+ end
79
+
80
+ def build_uri(path, params)
81
+ uri = configuration.base_uri.dup
82
+ uri.path = path
83
+ uri.query = URI.encode_www_form(params) unless params.empty?
84
+ uri
85
+ end
86
+
87
+ def build_http(uri)
88
+ http = Net::HTTP.new(uri.host, uri.port)
89
+ http.use_ssl = uri.scheme == "https"
90
+ http.read_timeout = configuration.timeout
91
+ http.open_timeout = configuration.timeout
92
+ http
93
+ end
94
+
95
+ def build_request(method, uri, body)
96
+ request_class = case method
97
+ when :get then Net::HTTP::Get
98
+ when :post then Net::HTTP::Post
99
+ when :patch then Net::HTTP::Patch
100
+ when :delete then Net::HTTP::Delete
101
+ else raise ArgumentError, "Unsupported HTTP method: #{method}"
102
+ end
103
+
104
+ request = request_class.new(uri)
105
+ request["x-api-key"] = configuration.api_key
106
+ request["Content-Type"] = "application/json"
107
+ request["Accept"] = "application/json"
108
+
109
+ if [:post, :patch].include?(method) && !body.empty?
110
+ request.body = JSON.generate(body)
111
+ end
112
+
113
+ request
114
+ end
115
+
116
+ def handle_response(response)
117
+ case response.code.to_i
118
+ when 200, 201
119
+ parse_json_response(response)
120
+ when 204
121
+ { success: true }
122
+ when 400
123
+ error = parse_json_response(response)
124
+ raise BadRequestError, error["error"] || error["message"] || "Bad request"
125
+ when 401
126
+ error = parse_json_response(response)
127
+ raise AuthenticationError, error["message"] || "Invalid access token"
128
+ when 404
129
+ raise NotFoundError, "Resource not found"
130
+ when 422
131
+ error = parse_json_response(response)
132
+ raise ValidationError, error["error"] || error["message"] || "Validation failed"
133
+ when 429
134
+ raise RateLimitError, "Rate limit exceeded"
135
+ when 500..599
136
+ raise ServerError, "Server error: #{response.code}"
137
+ else
138
+ raise ApiError, "Unexpected response: #{response.code}"
139
+ end
140
+ end
141
+
142
+ def parse_json_response(response)
143
+ return {} if response.body.nil? || response.body.empty?
144
+
145
+ JSON.parse(response.body)
146
+ rescue JSON::ParserError
147
+ { "raw_body" => response.body }
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module Tavus
6
+ class Configuration
7
+ attr_accessor :api_key, :base_url, :timeout
8
+
9
+ DEFAULT_BASE_URL = "https://tavusapi.com"
10
+ DEFAULT_TIMEOUT = 30
11
+
12
+ def initialize
13
+ @api_key = nil
14
+ @base_url = DEFAULT_BASE_URL
15
+ @timeout = DEFAULT_TIMEOUT
16
+ end
17
+
18
+ def valid?
19
+ !api_key.nil? && !api_key.empty?
20
+ end
21
+
22
+ def base_uri
23
+ URI.parse(base_url)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tavus
4
+ class Error < StandardError; end
5
+ class ConfigurationError < Error; end
6
+ class ApiError < Error; end
7
+ class AuthenticationError < ApiError; end
8
+ class BadRequestError < ApiError; end
9
+ class NotFoundError < ApiError; end
10
+ class ValidationError < ApiError; end
11
+ class RateLimitError < ApiError; end
12
+ class ServerError < ApiError; end
13
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tavus
4
+ module Resources
5
+ class Conversations
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ # Create a new conversation
11
+ # @param replica_id [String] The unique identifier for the replica
12
+ # @param persona_id [String] The unique identifier for the persona
13
+ # @param options [Hash] Additional optional parameters
14
+ # @option options [Boolean] :audio_only Specifies whether the interaction should be voice-only
15
+ # @option options [String] :callback_url A URL that will receive webhooks
16
+ # @option options [String] :conversation_name A name for the conversation
17
+ # @option options [String] :conversational_context Optional context for the conversation
18
+ # @option options [String] :custom_greeting Custom greeting for the replica
19
+ # @option options [Array<String>] :memory_stores Memory stores to use for the conversation
20
+ # @option options [Array<String>] :document_ids Document IDs the persona can access
21
+ # @option options [String] :document_retrieval_strategy Strategy for document retrieval (speed, quality, balanced)
22
+ # @option options [Array<String>] :document_tags Document tags the replica can access
23
+ # @option options [Boolean] :test_mode If true, conversation created but replica won't join
24
+ # @option options [Hash] :properties Optional properties to customize the conversation
25
+ # @return [Hash] The created conversation details
26
+ def create(replica_id: nil, persona_id: nil, **options)
27
+ body = options.dup
28
+ body[:replica_id] = replica_id if replica_id
29
+ body[:persona_id] = persona_id if persona_id
30
+
31
+ @client.post("/v2/conversations", body: body)
32
+ end
33
+
34
+ # Get a single conversation by ID
35
+ # @param conversation_id [String] The unique identifier of the conversation
36
+ # @return [Hash] The conversation details
37
+ def get(conversation_id)
38
+ @client.get("/v2/conversations/#{conversation_id}")
39
+ end
40
+
41
+ # List all conversations
42
+ # @param options [Hash] Query parameters
43
+ # @option options [Integer] :limit Number of conversations to return per page (default: 10)
44
+ # @option options [Integer] :page Page number to return (default: 1)
45
+ # @option options [String] :status Filter by status (active, ended)
46
+ # @return [Hash] List of conversations and total count
47
+ def list(**options)
48
+ @client.get("/v2/conversations", params: options)
49
+ end
50
+
51
+ # End a conversation
52
+ # @param conversation_id [String] The unique identifier of the conversation
53
+ # @return [Hash] Success response
54
+ def end(conversation_id)
55
+ @client.post("/v2/conversations/#{conversation_id}/end")
56
+ end
57
+
58
+ # Delete a conversation
59
+ # @param conversation_id [String] The unique identifier of the conversation
60
+ # @return [Hash] Success response
61
+ def delete(conversation_id)
62
+ @client.delete("/v2/conversations/#{conversation_id}")
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tavus
4
+ module Resources
5
+ class Documents
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ # Create a new document in the knowledge base
11
+ # @param document_url [String] The URL of the document to be processed (required)
12
+ # @param options [Hash] Additional optional parameters
13
+ # @option options [String] :document_name Optional name for the document
14
+ # @option options [String] :callback_url Optional URL for status updates
15
+ # @option options [Hash] :properties Optional key-value pairs for additional properties
16
+ # @option options [Array<String>] :tags Optional array of tags to categorize the document
17
+ # @return [Hash] The created document details
18
+ def create(document_url:, **options)
19
+ body = options.merge(document_url: document_url)
20
+ @client.post("/v2/documents", body: body)
21
+ end
22
+
23
+ # Get a single document by ID
24
+ # @param document_id [String] The unique identifier of the document
25
+ # @return [Hash] The document details
26
+ def get(document_id)
27
+ @client.get("/v2/documents/#{document_id}")
28
+ end
29
+
30
+ # List all documents
31
+ # @param options [Hash] Query parameters
32
+ # @option options [Integer] :limit Number of documents to return per page (default: 10)
33
+ # @option options [Integer] :page Page number for pagination (0-based, default: 0)
34
+ # @option options [String] :sort Sort direction (ascending, descending)
35
+ # @option options [String] :status Filter documents by status
36
+ # @option options [String] :name_or_uuid Search by name or UUID
37
+ # @option options [String] :tags Comma-separated list of tags to filter by
38
+ # @return [Hash] List of documents with pagination info
39
+ def list(**options)
40
+ @client.get("/v2/documents", params: options)
41
+ end
42
+
43
+ # Update a document's metadata
44
+ # @param document_id [String] The unique identifier of the document
45
+ # @param options [Hash] Update parameters
46
+ # @option options [String] :document_name New name for the document
47
+ # @option options [Array<String>] :tags New array of tags (overwrites existing)
48
+ # @return [Hash] The updated document details
49
+ def update(document_id, **options)
50
+ @client.patch("/v2/documents/#{document_id}", body: options)
51
+ end
52
+
53
+ # Delete a document
54
+ # @param document_id [String] The unique identifier of the document
55
+ # @return [Hash] Success response
56
+ def delete(document_id)
57
+ @client.delete("/v2/documents/#{document_id}")
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tavus
4
+ module Resources
5
+ class Guardrails
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ # Create new guardrails
11
+ # @param name [String] Descriptive name for the collection of guardrails
12
+ # @param data [Array<Hash>] Array of individual guardrails
13
+ # @return [Hash] The created guardrails details
14
+ def create(name: nil, data: [])
15
+ body = {}
16
+ body[:name] = name if name
17
+ body[:data] = data unless data.empty?
18
+
19
+ @client.post("/v2/guardrails", body: body)
20
+ end
21
+
22
+ # Get a single set of guardrails by ID
23
+ # @param guardrails_id [String] The unique identifier of the guardrails
24
+ # @return [Hash] The guardrails details
25
+ def get(guardrails_id)
26
+ @client.get("/v2/guardrails/#{guardrails_id}")
27
+ end
28
+
29
+ # List all guardrails
30
+ # @param options [Hash] Query parameters
31
+ # @option options [Integer] :limit Number of guardrails to return per page (default: 10)
32
+ # @option options [Integer] :page Page number to return (default: 1)
33
+ # @return [Hash] List of guardrails and total count
34
+ def list(**options)
35
+ @client.get("/v2/guardrails", params: options)
36
+ end
37
+
38
+ # Update guardrails using JSON Patch operations
39
+ # @param guardrails_id [String] The unique identifier of the guardrails
40
+ # @param operations [Array<Hash>] Array of JSON Patch operations
41
+ # @example
42
+ # operations = [
43
+ # { op: "replace", path: "/data/0/guardrails_prompt", value: "Your updated prompt" },
44
+ # { op: "add", path: "/data/0/callback_url", value: "https://your-server.com/webhook" }
45
+ # ]
46
+ # @return [Hash] Success response
47
+ def patch(guardrails_id, operations)
48
+ raise ArgumentError, "Operations must be an array" unless operations.is_a?(Array)
49
+ raise ArgumentError, "Operations cannot be empty" if operations.empty?
50
+
51
+ operations.each do |op|
52
+ raise ArgumentError, "Each operation must have 'op' and 'path'" unless op[:op] && op[:path]
53
+ raise ArgumentError, "Operation 'op' must be one of: add, remove, replace, copy, move, test" unless %w[add remove replace copy move test].include?(op[:op])
54
+ end
55
+
56
+ @client.patch("/v2/guardrails/#{guardrails_id}", body: operations)
57
+ end
58
+
59
+ # Helper method to build patch operations
60
+ # @param field [String] The field path
61
+ # @param value [Object] The value to set
62
+ # @param operation [String] The operation type (default: "replace")
63
+ # @return [Hash] A JSON Patch operation
64
+ def build_patch_operation(field, value, operation: "replace")
65
+ op = { op: operation, path: field }
66
+ op[:value] = value unless operation == "remove"
67
+ op
68
+ end
69
+
70
+ # Convenient method to update a field
71
+ # @param guardrails_id [String] The unique identifier of the guardrails
72
+ # @param field [String] The field path to update
73
+ # @param value [Object] The new value
74
+ # @return [Hash] Success response
75
+ def update_field(guardrails_id, field, value)
76
+ operation = build_patch_operation(field, value, operation: "replace")
77
+ patch(guardrails_id, [operation])
78
+ end
79
+
80
+ # Delete guardrails
81
+ # @param guardrails_id [String] The unique identifier of the guardrails
82
+ # @return [Hash] Success response
83
+ def delete(guardrails_id)
84
+ @client.delete("/v2/guardrails/#{guardrails_id}")
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tavus
4
+ module Resources
5
+ class Objectives
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ # Create new objectives
11
+ # @param data [Array<Hash>] Array of objectives to create
12
+ # @return [Hash] The created objective details
13
+ def create(data:)
14
+ raise ArgumentError, "data must be an array" unless data.is_a?(Array)
15
+ raise ArgumentError, "data cannot be empty" if data.empty?
16
+
17
+ @client.post("/v2/objectives", body: { data: data })
18
+ end
19
+
20
+ # Get a single objective by ID
21
+ # @param objectives_id [String] The unique identifier of the objective
22
+ # @return [Hash] The objective details
23
+ def get(objectives_id)
24
+ @client.get("/v2/objectives/#{objectives_id}")
25
+ end
26
+
27
+ # List all objectives
28
+ # @param options [Hash] Query parameters
29
+ # @option options [Integer] :limit Number of objectives to return per page (default: 10)
30
+ # @option options [Integer] :page Page number to return (default: 1)
31
+ # @return [Hash] List of objectives and total count
32
+ def list(**options)
33
+ @client.get("/v2/objectives", params: options)
34
+ end
35
+
36
+ # Update an objective using JSON Patch operations
37
+ # @param objectives_id [String] The unique identifier of the objective
38
+ # @param operations [Array<Hash>] Array of JSON Patch operations
39
+ # @example
40
+ # operations = [
41
+ # { op: "replace", path: "/data/0/objective_name", value: "updated_objective_name" },
42
+ # { op: "replace", path: "/data/0/objective_prompt", value: "Updated prompt" },
43
+ # { op: "add", path: "/data/0/output_variables", value: ["new_variable"] }
44
+ # ]
45
+ # @return [Hash] Success response
46
+ def patch(objectives_id, operations)
47
+ raise ArgumentError, "Operations must be an array" unless operations.is_a?(Array)
48
+ raise ArgumentError, "Operations cannot be empty" if operations.empty?
49
+
50
+ operations.each do |op|
51
+ raise ArgumentError, "Each operation must have 'op' and 'path'" unless op[:op] && op[:path]
52
+ raise ArgumentError, "Operation 'op' must be one of: add, remove, replace, copy, move, test" unless %w[add remove replace copy move test].include?(op[:op])
53
+ end
54
+
55
+ @client.patch("/v2/objectives/#{objectives_id}", body: operations)
56
+ end
57
+
58
+ # Helper method to build patch operations
59
+ # @param field [String] The field path
60
+ # @param value [Object] The value to set
61
+ # @param operation [String] The operation type (default: "replace")
62
+ # @return [Hash] A JSON Patch operation
63
+ def build_patch_operation(field, value, operation: "replace")
64
+ op = { op: operation, path: field }
65
+ op[:value] = value unless operation == "remove"
66
+ op
67
+ end
68
+
69
+ # Convenient method to update a field
70
+ # @param objectives_id [String] The unique identifier of the objective
71
+ # @param field [String] The field path to update
72
+ # @param value [Object] The new value
73
+ # @return [Hash] Success response
74
+ def update_field(objectives_id, field, value)
75
+ operation = build_patch_operation(field, value, operation: "replace")
76
+ patch(objectives_id, [operation])
77
+ end
78
+
79
+ # Delete an objective
80
+ # @param objectives_id [String] The unique identifier of the objective
81
+ # @return [Hash] Success response
82
+ def delete(objectives_id)
83
+ @client.delete("/v2/objectives/#{objectives_id}")
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tavus
4
+ module Resources
5
+ class Personas
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ # Create a new persona
11
+ # @param system_prompt [String] The system prompt for the LLM (required for full pipeline mode)
12
+ # @param options [Hash] Additional optional parameters
13
+ # @option options [String] :persona_name A name for the persona
14
+ # @option options [String] :pipeline_mode Pipeline mode (full, echo)
15
+ # @option options [String] :context Context for the LLM
16
+ # @option options [String] :default_replica_id Default replica ID for the persona
17
+ # @option options [Array<String>] :document_ids Document IDs the persona can access
18
+ # @option options [Array<String>] :document_tags Document tags the persona can access
19
+ # @option options [Hash] :layers Configuration for perception, STT, LLM, TTS layers
20
+ # @return [Hash] The created persona details
21
+ def create(system_prompt: nil, **options)
22
+ body = options.dup
23
+ body[:system_prompt] = system_prompt if system_prompt
24
+
25
+ @client.post("/v2/personas", body: body)
26
+ end
27
+
28
+ # Get a single persona by ID
29
+ # @param persona_id [String] The unique identifier of the persona
30
+ # @return [Hash] The persona details
31
+ def get(persona_id)
32
+ @client.get("/v2/personas/#{persona_id}")
33
+ end
34
+
35
+ # List all personas
36
+ # @param options [Hash] Query parameters
37
+ # @option options [Integer] :limit Number of personas to return per page (default: 10)
38
+ # @option options [Integer] :page Page number to return (default: 1)
39
+ # @option options [String] :persona_type Filter by type (user, system)
40
+ # @return [Hash] List of personas and total count
41
+ def list(**options)
42
+ @client.get("/v2/personas", params: options)
43
+ end
44
+
45
+ # Update a persona using JSON Patch operations
46
+ # @param persona_id [String] The unique identifier of the persona
47
+ # @param operations [Array<Hash>] Array of JSON Patch operations
48
+ # @example
49
+ # operations = [
50
+ # { op: "replace", path: "/persona_name", value: "Wellness Advisor" },
51
+ # { op: "add", path: "/layers/tts/tts_emotion_control", value: "true" },
52
+ # { op: "remove", path: "/layers/stt/hotwords" }
53
+ # ]
54
+ # @return [Hash] Success response
55
+ def patch(persona_id, operations)
56
+ raise ArgumentError, "Operations must be an array" unless operations.is_a?(Array)
57
+ raise ArgumentError, "Operations cannot be empty" if operations.empty?
58
+
59
+ # Validate each operation has required fields
60
+ operations.each do |op|
61
+ raise ArgumentError, "Each operation must have 'op' and 'path'" unless op[:op] && op[:path]
62
+ raise ArgumentError, "Operation 'op' must be one of: add, remove, replace, copy, move, test" unless %w[add remove replace copy move test].include?(op[:op])
63
+ end
64
+
65
+ @client.patch("/v2/personas/#{persona_id}", body: operations)
66
+ end
67
+
68
+ # Helper method to build patch operations
69
+ # @param field [String] The field path (e.g., "/persona_name", "/layers/llm/model")
70
+ # @param value [Object] The value to set
71
+ # @param operation [String] The operation type (default: "replace")
72
+ # @return [Hash] A JSON Patch operation
73
+ def build_patch_operation(field, value, operation: "replace")
74
+ op = { op: operation, path: field }
75
+ op[:value] = value unless operation == "remove"
76
+ op
77
+ end
78
+
79
+ # Convenient method to replace a field
80
+ # @param persona_id [String] The unique identifier of the persona
81
+ # @param field [String] The field path to update
82
+ # @param value [Object] The new value
83
+ # @return [Hash] Success response
84
+ def update_field(persona_id, field, value)
85
+ operation = build_patch_operation(field, value, operation: "replace")
86
+ patch(persona_id, [operation])
87
+ end
88
+
89
+ # Delete a persona
90
+ # @param persona_id [String] The unique identifier of the persona
91
+ # @return [Hash] Success response
92
+ def delete(persona_id)
93
+ @client.delete("/v2/personas/#{persona_id}")
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tavus
4
+ module Resources
5
+ class Replicas
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ # Create a new replica
11
+ # @param train_video_url [String] Direct link to training video (required)
12
+ # @param options [Hash] Additional optional parameters
13
+ # @option options [String] :consent_video_url Direct link to consent video
14
+ # @option options [String] :callback_url URL to receive callbacks on completion
15
+ # @option options [String] :replica_name Name for the replica
16
+ # @option options [String] :model_name Phoenix model version (default: phoenix-3)
17
+ # @option options [Hash] :properties Additional properties
18
+ # @return [Hash] The created replica details
19
+ def create(train_video_url:, **options)
20
+ body = options.merge(train_video_url: train_video_url)
21
+ @client.post("/v2/replicas", body: body)
22
+ end
23
+
24
+ # Get a single replica by ID
25
+ # @param replica_id [String] The unique identifier of the replica
26
+ # @param verbose [Boolean] Include additional replica data (default: false)
27
+ # @return [Hash] The replica details
28
+ def get(replica_id, verbose: false)
29
+ params = verbose ? { verbose: true } : {}
30
+ @client.get("/v2/replicas/#{replica_id}", params: params)
31
+ end
32
+
33
+ # List all replicas
34
+ # @param options [Hash] Query parameters
35
+ # @option options [Integer] :limit Number of replicas to return per page
36
+ # @option options [Integer] :page Page number to return
37
+ # @option options [Boolean] :verbose Include additional replica data
38
+ # @option options [String] :replica_type Filter by type (user, system)
39
+ # @option options [String] :replica_ids Comma-separated list of replica IDs to filter
40
+ # @return [Hash] List of replicas and total count
41
+ def list(**options)
42
+ @client.get("/v2/replicas", params: options)
43
+ end
44
+
45
+ # Delete a replica
46
+ # @param replica_id [String] The unique identifier of the replica
47
+ # @param hard [Boolean] If true, hard delete replica and assets (irreversible)
48
+ # @return [Hash] Success response
49
+ def delete(replica_id, hard: false)
50
+ params = hard ? { hard: true } : {}
51
+ @client.delete("/v2/replicas/#{replica_id}", params: params)
52
+ end
53
+
54
+ # Rename a replica
55
+ # @param replica_id [String] The unique identifier of the replica
56
+ # @param replica_name [String] New name for the replica
57
+ # @return [Hash] Success response
58
+ def rename(replica_id, replica_name)
59
+ @client.patch("/v2/replicas/#{replica_id}/name", body: { replica_name: replica_name })
60
+ end
61
+ end
62
+ end
63
+ end
64
+