mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-13 23:11:40 +00:00
fix(terraform): send a vector_store_id on create and unwrap the info envelope
Creating a litellm_vector_store never worked. /vector_store/new requires vector_store_id, but the attribute is computed and the provider left it unset, so the proxy rejected every create. The resource was also keyed on the store's name instead of its id, and read unmarshalled /vector_store/info straight into VectorStoreResponse even though the proxy nests the store under "vector_store", so every attribute read back empty. Mint a UUID for the new store, key the resource on it, and decode the info envelope.
This commit is contained in:
parent
9a715df212
commit
0e2872c98b
2 changed files with 75 additions and 6 deletions
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
|
||||
)
|
||||
|
||||
|
|
@ -29,7 +30,12 @@ func resourceLiteLLMVectorStoreCreate(d *schema.ResourceData, m interface{}) err
|
|||
paramsMap[k] = v
|
||||
}
|
||||
|
||||
// /vector_store/new rejects a missing vector_store_id, and the attribute is
|
||||
// computed, so the provider has to mint the id it stores under.
|
||||
vectorStoreID := uuid.New().String()
|
||||
|
||||
vectorStoreRequest := VectorStoreRequest{
|
||||
VectorStoreID: vectorStoreID,
|
||||
CustomLLMProvider: customLLMProvider,
|
||||
VectorStoreName: vectorStoreName,
|
||||
VectorStoreDescription: vectorStoreDescription,
|
||||
|
|
@ -49,9 +55,7 @@ func resourceLiteLLMVectorStoreCreate(d *schema.ResourceData, m interface{}) err
|
|||
return fmt.Errorf("failed to create vector store: %w", err)
|
||||
}
|
||||
|
||||
// Set the resource ID to the vector store name for now
|
||||
// We'll update this after reading the response to get the actual ID
|
||||
d.SetId(vectorStoreName)
|
||||
d.SetId(vectorStoreID)
|
||||
|
||||
return resourceLiteLLMVectorStoreRead(d, m)
|
||||
}
|
||||
|
|
@ -76,8 +80,11 @@ func resourceLiteLLMVectorStoreRead(d *schema.ResourceData, m interface{}) error
|
|||
return nil
|
||||
}
|
||||
|
||||
var vectorStoreResp VectorStoreResponse
|
||||
err = handleVectorStoreAPIResponse(resp, &vectorStoreResp, client)
|
||||
// /vector_store/info nests the store under "vector_store".
|
||||
var infoResp struct {
|
||||
VectorStore VectorStoreResponse `json:"vector_store"`
|
||||
}
|
||||
err = handleVectorStoreAPIResponse(resp, &infoResp, client)
|
||||
if err != nil {
|
||||
if err.Error() == "vector_store_not_found" {
|
||||
d.SetId("")
|
||||
|
|
@ -85,6 +92,7 @@ func resourceLiteLLMVectorStoreRead(d *schema.ResourceData, m interface{}) error
|
|||
}
|
||||
return fmt.Errorf("failed to read vector store: %w", err)
|
||||
}
|
||||
vectorStoreResp := infoResp.VectorStore
|
||||
|
||||
// Update the resource ID to the actual vector store ID from the response
|
||||
if vectorStoreResp.VectorStoreID != "" {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ func TestVectorStoreReadDoesNotPersistServerLitellmParams(t *testing.T) {
|
|||
"api_base": "https://upstream.example.com",
|
||||
},
|
||||
}
|
||||
body, _ := json.Marshal(resp)
|
||||
body, _ := json.Marshal(map[string]interface{}{"vector_store": resp})
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
|
@ -53,3 +53,64 @@ func TestVectorStoreReadDoesNotPersistServerLitellmParams(t *testing.T) {
|
|||
t.Fatalf("read did not populate non-sensitive fields")
|
||||
}
|
||||
}
|
||||
|
||||
// /vector_store/new 400s without a vector_store_id, and the attribute is
|
||||
// computed, so create has to mint one and key the resource on it rather than on
|
||||
// the store's name.
|
||||
func TestVectorStoreCreateSendsGeneratedIDAndUsesItAsResourceID(t *testing.T) {
|
||||
var createBody map[string]interface{}
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if r.URL.Path == "/vector_store/new" {
|
||||
json.NewDecoder(r.Body).Decode(&createBody)
|
||||
id, _ := createBody["vector_store_id"].(string)
|
||||
if id == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(`{"detail":"vector_store_id and custom_llm_provider are required"}`))
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"status": "success",
|
||||
"vector_store": VectorStoreResponse{VectorStoreID: id, VectorStoreName: "kb", CustomLLMProvider: "openai"},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var info VectorStoreInfoRequest
|
||||
json.NewDecoder(r.Body).Decode(&info)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"vector_store": VectorStoreResponse{
|
||||
VectorStoreID: info.VectorStoreID,
|
||||
VectorStoreName: "kb",
|
||||
CustomLLMProvider: "openai",
|
||||
CreatedAt: "2026-01-01T00:00:00Z",
|
||||
},
|
||||
})
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
d := schema.TestResourceDataRaw(t, resourceLiteLLMVectorStore().Schema, map[string]interface{}{
|
||||
"vector_store_name": "kb",
|
||||
"custom_llm_provider": "openai",
|
||||
})
|
||||
|
||||
if err := resourceLiteLLMVectorStoreCreate(d, NewClient(srv.URL, "test-key", true)); err != nil {
|
||||
t.Fatalf("create failed: %v", err)
|
||||
}
|
||||
|
||||
sentID, _ := createBody["vector_store_id"].(string)
|
||||
if sentID == "" {
|
||||
t.Fatal("create payload omitted vector_store_id, which the proxy rejects")
|
||||
}
|
||||
if d.Id() != sentID {
|
||||
t.Errorf("resource id = %q, want the created store id %q", d.Id(), sentID)
|
||||
}
|
||||
if d.Get("vector_store_id").(string) != sentID {
|
||||
t.Errorf("vector_store_id = %q, want %q", d.Get("vector_store_id").(string), sentID)
|
||||
}
|
||||
if d.Get("created_at").(string) != "2026-01-01T00:00:00Z" {
|
||||
t.Errorf("create did not refresh computed fields from the API: %v", d.Get("created_at"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue