Ever wanted to make an LLM call on a segment? Redshred has a feature that allows you to do this called, the “LLM Microservice”. For any segment that has been created, you can pass the segment link to the LLM microservice, along with the LLM prompt, and get back the response from the LLM. There is additional support to pass the image crop of the segment to the LLM.
The LLM Microservice endpoint is:
https://api.staging.redshred.com/v2/collections/{collection_name}/services/llm-query
Sample API Call to invoke the endpoint:
# Set up environment variables
export REDSHRED_HOST="https://api.staging.redshred.com"
export COLLECTION="tracee"
export REDSHRED_TOKEN="<token>"
export SEGMENT_ID="HYSiSRBdFAsugxh5aWEeik"
# Create a temporary file with the JSON payload
cat > llm_request.json << 'EOL'
{
"collection": "$COLLECTION",
"segment_id": "$SEGMENT_ID",
"prompt": "Please extract the formula from the attached image in LaTeX format.",
"model_config": {
"model": "gpt-4o-mini",
"temperature": 0.5,
"max_tokens": 256
},
"segment_type": "llm_extract",
"image_attachment": "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"
}
EOL
# Make the POST request
curl -X POST \
"$REDSHRED_HOST/v2/collections/$COLLECTION/services/llm-query" \
-H "Authorization: Token $REDSHRED_TOKEN" \
-H "Content-Type: application/json" \
-d @llm_request.json \
--output llm_response.json
# Clean up
rm llm_request.json
# TODO: Add code for python example
There is a complete runnable example of this code in random-go.
The code queries for segments of type formula
and prompts the LLM to extract the formula from the attached image in LaTeX format. It relies on the tracee
collection on api.staging.redshred.com
.
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"github.com/redshred/redshred-client-go/pkg/redshred"
)
// Model config as a package-level constant
var defaultModelCfg = ModelConfig{
Model: "gpt-4o-mini",
Temperature: 0.5,
MaxTokens: 256,
}
type (
LLMQueryRequest struct {
Collection string `json:"collection"`
SegmentID string `json:"segment_id"`
Prompt string `json:"prompt"`
ModelConfig ModelConfig `json:"model_config"`
SegmentType string `json:"segment_type"`
ImageAttachment string `json:"image_attachment,omitempty"`
}
ModelConfig struct {
Model string `json:"model"`
Temperature float64 `json:"temperature"`
MaxTokens int `json:"max_tokens"`
}
LLMQueryResponse struct {
Prediction string `json:"prediction"`
SegmentLink string `json:"segment_link"`
RawOutput string `json:"raw_output"`
}
)
// processSegment processes a single segment, downloading its crop and sending to LLM
func processSegment(rs redshred.RedShredClient, coll *redshred.Collection, segment redshred.Segment) error {
// Construct the crop image URL
imageURL := fmt.Sprintf("%s/v2/collections/%s/services/crop_by_link?segment_link=%s",
rs.BaseUrl, coll.Name, segment.SelfLink)
// Download the crop image
imgData, err := DownloadImage(&rs, imageURL)
if err != nil {
return fmt.Errorf("downloading crop image: %w", err)
}
// Process the image with LLM
if err := processImage(rs, coll, segment, imgData); err != nil {
return fmt.Errorf("processing image: %w", err)
}
return nil
}
func processImage(rs redshred.RedShredClient, coll *redshred.Collection, segment redshred.Segment, imgData []byte) error {
reqBody := LLMQueryRequest{
Collection: coll.Name,
SegmentID: segment.ID,
Prompt: "Please extract the formula from the attached image in LaTeX format.",
ModelConfig: defaultModelCfg,
SegmentType: "llm_extract",
ImageAttachment: base64.StdEncoding.EncodeToString(imgData),
}
reqJSON, err := json.Marshal(reqBody)
if err != nil {
return fmt.Errorf("marshaling request: %w", err)
}
url := fmt.Sprintf("%s/v2/collections/%s/services/llm_query", rs.BaseUrl, coll.Name)
resp, err := rs.JSONRequest("POST", url, bytes.NewReader(reqJSON))
if err != nil {
return fmt.Errorf("LLM query: %w", err)
}
body, err := io.ReadAll(bytes.NewReader(resp))
if err != nil {
return fmt.Errorf("reading response: %w", err)
}
var llmResp LLMQueryResponse
if err := json.Unmarshal(body, &llmResp); err != nil {
return fmt.Errorf("parsing response: %w", err)
}
if llmResp.RawOutput == "" {
return fmt.Errorf("empty response, prediction: %s", llmResp.Prediction)
}
fmt.Printf("\nExtracted formula from %s:\n%s\nSegment Link: %s\n",
segment.ID, llmResp.RawOutput, llmResp.SegmentLink)
return nil
}