@@ -6,7 +6,6 @@ use axum::{
6
6
Json , Router , ServiceExt ,
7
7
} ;
8
8
use chroma_system:: System ;
9
- use chroma_types:: RawWhereFields ;
10
9
use chroma_types:: {
11
10
AddCollectionRecordsResponse , ChecklistResponse , Collection , CollectionConfiguration ,
12
11
CollectionMetadataUpdate , CollectionUuid , CountCollectionsRequest , CountCollectionsResponse ,
@@ -20,6 +19,7 @@ use chroma_types::{
20
19
UpdateCollectionConfiguration , UpdateCollectionRecordsResponse , UpdateCollectionResponse ,
21
20
UpdateMetadata , UpsertCollectionRecordsResponse ,
22
21
} ;
22
+ use chroma_types:: { ForkCollectionResponse , RawWhereFields } ;
23
23
use mdac:: { Rule , Scorecard , ScorecardTicket } ;
24
24
use opentelemetry:: global;
25
25
use opentelemetry:: metrics:: { Counter , Meter } ;
@@ -93,6 +93,7 @@ pub struct Metrics {
93
93
get_collection : Counter < u64 > ,
94
94
update_collection : Counter < u64 > ,
95
95
delete_collection : Counter < u64 > ,
96
+ fork_collection : Counter < u64 > ,
96
97
collection_add : Counter < u64 > ,
97
98
collection_update : Counter < u64 > ,
98
99
collection_upsert : Counter < u64 > ,
@@ -123,6 +124,7 @@ impl Metrics {
123
124
get_collection : meter. u64_counter ( "get_collection" ) . build ( ) ,
124
125
update_collection : meter. u64_counter ( "update_collection" ) . build ( ) ,
125
126
delete_collection : meter. u64_counter ( "delete_collection" ) . build ( ) ,
127
+ fork_collection : meter. u64_counter ( "fork_collection" ) . build ( ) ,
126
128
collection_add : meter. u64_counter ( "collection_add" ) . build ( ) ,
127
129
collection_update : meter. u64_counter ( "collection_update" ) . build ( ) ,
128
130
collection_upsert : meter. u64_counter ( "collection_upsert" ) . build ( ) ,
@@ -232,6 +234,10 @@ impl FrontendServer {
232
234
. put ( update_collection)
233
235
. delete ( delete_collection) ,
234
236
)
237
+ . route (
238
+ "/api/v2/tenants/{tenant}/databases/{database}/collections/{collection_id}/fork" ,
239
+ post ( fork_collection) ,
240
+ )
235
241
. route (
236
242
"/api/v2/tenants/{tenant}/databases/{database}/collections/{collection_id}/add" ,
237
243
post ( collection_add) ,
@@ -1090,6 +1096,70 @@ async fn delete_collection(
1090
1096
Ok ( Json ( UpdateCollectionResponse { } ) )
1091
1097
}
1092
1098
1099
+ #[ derive( Deserialize , Serialize , ToSchema , Debug , Clone ) ]
1100
+ pub struct ForkCollectionPayload {
1101
+ pub new_name : String ,
1102
+ }
1103
+
1104
+ /// Forks an existing collection.
1105
+ #[ utoipa:: path(
1106
+ post,
1107
+ path = "/api/v2/tenants/{tenant}/databases/{database}/collections/{collection_id}/fork" ,
1108
+ request_body = ForkCollectionPayload ,
1109
+ responses(
1110
+ ( status = 200 , description = "Collection forked successfully" , body = ForkCollectionResponse ) ,
1111
+ ( status = 401 , description = "Unauthorized" , body = ErrorResponse ) ,
1112
+ ( status = 404 , description = "Collection not found" , body = ErrorResponse ) ,
1113
+ ( status = 500 , description = "Server error" , body = ErrorResponse )
1114
+ ) ,
1115
+ params(
1116
+ ( "tenant" = String , Path , description = "Tenant ID" ) ,
1117
+ ( "database" = String , Path , description = "Database name" ) ,
1118
+ ( "collection_id" = String , Path , description = "UUID of the collection to update" )
1119
+ )
1120
+ ) ]
1121
+ async fn fork_collection (
1122
+ headers : HeaderMap ,
1123
+ Path ( ( tenant, database, collection_id) ) : Path < ( String , String , String ) > ,
1124
+ State ( mut server) : State < FrontendServer > ,
1125
+ Json ( payload) : Json < ForkCollectionPayload > ,
1126
+ ) -> Result < Json < ForkCollectionResponse > , ServerError > {
1127
+ server. metrics . fork_collection . add (
1128
+ 1 ,
1129
+ & [
1130
+ KeyValue :: new ( "tenant" , tenant. clone ( ) ) ,
1131
+ KeyValue :: new ( "collection_id" , collection_id. clone ( ) ) ,
1132
+ ] ,
1133
+ ) ;
1134
+ tracing:: info!(
1135
+ "Forking collection [{collection_id}] in database [{database}] for tenant [{tenant}]"
1136
+ ) ;
1137
+ server
1138
+ . authenticate_and_authorize (
1139
+ & headers,
1140
+ AuthzAction :: ForkCollection ,
1141
+ AuthzResource {
1142
+ tenant : Some ( tenant. clone ( ) ) ,
1143
+ database : Some ( database. clone ( ) ) ,
1144
+ collection : Some ( collection_id. clone ( ) ) ,
1145
+ } ,
1146
+ )
1147
+ . await ?;
1148
+ let _guard =
1149
+ server. scorecard_request ( & [ "op:fork_collection" , format ! ( "tenant:{}" , tenant) . as_str ( ) ] ) ;
1150
+ let collection_id =
1151
+ CollectionUuid :: from_str ( & collection_id) . map_err ( |_| ValidationError :: CollectionId ) ?;
1152
+
1153
+ let request = chroma_types:: ForkCollectionRequest :: try_new (
1154
+ tenant,
1155
+ database,
1156
+ collection_id,
1157
+ payload. new_name ,
1158
+ ) ?;
1159
+
1160
+ Ok ( Json ( server. frontend . fork_collection ( request) . await ?) )
1161
+ }
1162
+
1093
1163
#[ derive( Serialize , Deserialize , ToSchema , Debug , Clone ) ]
1094
1164
pub struct AddCollectionRecordsPayload {
1095
1165
ids : Vec < String > ,
0 commit comments