PlanOpticon

planopticon / video_processor / providers / bedrock_provider.py
Source Blame History 193 lines
0981a08… noreply 1 """AWS Bedrock provider implementation."""
0981a08… noreply 2
0981a08… noreply 3 import base64
0981a08… noreply 4 import json
0981a08… noreply 5 import logging
0981a08… noreply 6 import os
0981a08… noreply 7 from pathlib import Path
0981a08… noreply 8 from typing import Optional
0981a08… noreply 9
0981a08… noreply 10 from dotenv import load_dotenv
0981a08… noreply 11
0981a08… noreply 12 from video_processor.providers.base import BaseProvider, ModelInfo, ProviderRegistry
0981a08… noreply 13
0981a08… noreply 14 load_dotenv()
0981a08… noreply 15 logger = logging.getLogger(__name__)
0981a08… noreply 16
0981a08… noreply 17 # Curated list of popular Bedrock models
0981a08… noreply 18 _BEDROCK_MODELS = [
0981a08… noreply 19 ModelInfo(
0981a08… noreply 20 id="anthropic.claude-3-5-sonnet-20241022-v2:0",
0981a08… noreply 21 provider="bedrock",
0981a08… noreply 22 display_name="Claude 3.5 Sonnet v2",
0981a08… noreply 23 capabilities=["chat", "vision"],
0981a08… noreply 24 ),
0981a08… noreply 25 ModelInfo(
0981a08… noreply 26 id="anthropic.claude-3-sonnet-20240229-v1:0",
0981a08… noreply 27 provider="bedrock",
0981a08… noreply 28 display_name="Claude 3 Sonnet",
0981a08… noreply 29 capabilities=["chat", "vision"],
0981a08… noreply 30 ),
0981a08… noreply 31 ModelInfo(
0981a08… noreply 32 id="anthropic.claude-3-haiku-20240307-v1:0",
0981a08… noreply 33 provider="bedrock",
0981a08… noreply 34 display_name="Claude 3 Haiku",
0981a08… noreply 35 capabilities=["chat", "vision"],
0981a08… noreply 36 ),
0981a08… noreply 37 ModelInfo(
0981a08… noreply 38 id="amazon.titan-text-express-v1",
0981a08… noreply 39 provider="bedrock",
0981a08… noreply 40 display_name="Amazon Titan Text Express",
0981a08… noreply 41 capabilities=["chat"],
0981a08… noreply 42 ),
0981a08… noreply 43 ModelInfo(
0981a08… noreply 44 id="meta.llama3-70b-instruct-v1:0",
0981a08… noreply 45 provider="bedrock",
0981a08… noreply 46 display_name="Llama 3 70B Instruct",
0981a08… noreply 47 capabilities=["chat"],
0981a08… noreply 48 ),
0981a08… noreply 49 ModelInfo(
0981a08… noreply 50 id="mistral.mistral-large-2402-v1:0",
0981a08… noreply 51 provider="bedrock",
0981a08… noreply 52 display_name="Mistral Large",
0981a08… noreply 53 capabilities=["chat"],
0981a08… noreply 54 ),
0981a08… noreply 55 ]
0981a08… noreply 56
0981a08… noreply 57
0981a08… noreply 58 class BedrockProvider(BaseProvider):
0981a08… noreply 59 """AWS Bedrock provider using boto3."""
0981a08… noreply 60
0981a08… noreply 61 provider_name = "bedrock"
0981a08… noreply 62
0981a08… noreply 63 def __init__(
0981a08… noreply 64 self,
0981a08… noreply 65 aws_access_key_id: Optional[str] = None,
0981a08… noreply 66 aws_secret_access_key: Optional[str] = None,
0981a08… noreply 67 region_name: Optional[str] = None,
0981a08… noreply 68 ):
0981a08… noreply 69 try:
0981a08… noreply 70 import boto3
0981a08… noreply 71 except ImportError:
0981a08… noreply 72 raise ImportError("boto3 package not installed. Install with: pip install boto3")
0981a08… noreply 73
0981a08… noreply 74 self._boto3 = boto3
0981a08… noreply 75 self._region = region_name or os.getenv("AWS_DEFAULT_REGION", "us-east-1")
0981a08… noreply 76 self._client = boto3.client(
0981a08… noreply 77 "bedrock-runtime",
0981a08… noreply 78 aws_access_key_id=aws_access_key_id or os.getenv("AWS_ACCESS_KEY_ID"),
0981a08… noreply 79 aws_secret_access_key=aws_secret_access_key or os.getenv("AWS_SECRET_ACCESS_KEY"),
0981a08… noreply 80 region_name=self._region,
0981a08… noreply 81 )
0981a08… noreply 82 self._last_usage = {}
0981a08… noreply 83
0981a08… noreply 84 def chat(
0981a08… noreply 85 self,
0981a08… noreply 86 messages: list[dict],
0981a08… noreply 87 max_tokens: int = 4096,
0981a08… noreply 88 temperature: float = 0.7,
0981a08… noreply 89 model: Optional[str] = None,
0981a08… noreply 90 ) -> str:
0981a08… noreply 91 model = model or "anthropic.claude-3-sonnet-20240229-v1:0"
0981a08… noreply 92 # Strip bedrock/ prefix if present
0981a08… noreply 93 if model.startswith("bedrock/"):
0981a08… noreply 94 model = model[len("bedrock/") :]
0981a08… noreply 95
0981a08… noreply 96 body = json.dumps(
0981a08… noreply 97 {
0981a08… noreply 98 "anthropic_version": "bedrock-2023-05-31",
0981a08… noreply 99 "max_tokens": max_tokens,
0981a08… noreply 100 "temperature": temperature,
0981a08… noreply 101 "messages": messages,
0981a08… noreply 102 }
0981a08… noreply 103 )
0981a08… noreply 104
0981a08… noreply 105 response = self._client.invoke_model(
0981a08… noreply 106 modelId=model,
0981a08… noreply 107 contentType="application/json",
0981a08… noreply 108 accept="application/json",
0981a08… noreply 109 body=body,
0981a08… noreply 110 )
0981a08… noreply 111
0981a08… noreply 112 result = json.loads(response["body"].read())
0981a08… noreply 113 self._last_usage = {
0981a08… noreply 114 "input_tokens": result.get("usage", {}).get("input_tokens", 0),
0981a08… noreply 115 "output_tokens": result.get("usage", {}).get("output_tokens", 0),
0981a08… noreply 116 }
0981a08… noreply 117 return result.get("content", [{}])[0].get("text", "")
0981a08… noreply 118
0981a08… noreply 119 def analyze_image(
0981a08… noreply 120 self,
0981a08… noreply 121 image_bytes: bytes,
0981a08… noreply 122 prompt: str,
0981a08… noreply 123 max_tokens: int = 4096,
0981a08… noreply 124 model: Optional[str] = None,
0981a08… noreply 125 ) -> str:
0981a08… noreply 126 model = model or "anthropic.claude-3-sonnet-20240229-v1:0"
0981a08… noreply 127 if model.startswith("bedrock/"):
0981a08… noreply 128 model = model[len("bedrock/") :]
0981a08… noreply 129
0981a08… noreply 130 b64 = base64.b64encode(image_bytes).decode()
0981a08… noreply 131 body = json.dumps(
0981a08… noreply 132 {
0981a08… noreply 133 "anthropic_version": "bedrock-2023-05-31",
0981a08… noreply 134 "max_tokens": max_tokens,
0981a08… noreply 135 "messages": [
0981a08… noreply 136 {
0981a08… noreply 137 "role": "user",
0981a08… noreply 138 "content": [
0981a08… noreply 139 {
0981a08… noreply 140 "type": "image",
0981a08… noreply 141 "source": {
0981a08… noreply 142 "type": "base64",
0981a08… noreply 143 "media_type": "image/jpeg",
0981a08… noreply 144 "data": b64,
0981a08… noreply 145 },
0981a08… noreply 146 },
0981a08… noreply 147 {"type": "text", "text": prompt},
0981a08… noreply 148 ],
0981a08… noreply 149 }
0981a08… noreply 150 ],
0981a08… noreply 151 }
0981a08… noreply 152 )
0981a08… noreply 153
0981a08… noreply 154 response = self._client.invoke_model(
0981a08… noreply 155 modelId=model,
0981a08… noreply 156 contentType="application/json",
0981a08… noreply 157 accept="application/json",
0981a08… noreply 158 body=body,
0981a08… noreply 159 )
0981a08… noreply 160
0981a08… noreply 161 result = json.loads(response["body"].read())
0981a08… noreply 162 self._last_usage = {
0981a08… noreply 163 "input_tokens": result.get("usage", {}).get("input_tokens", 0),
0981a08… noreply 164 "output_tokens": result.get("usage", {}).get("output_tokens", 0),
0981a08… noreply 165 }
0981a08… noreply 166 return result.get("content", [{}])[0].get("text", "")
0981a08… noreply 167
0981a08… noreply 168 def transcribe_audio(
0981a08… noreply 169 self,
0981a08… noreply 170 audio_path: str | Path,
0981a08… noreply 171 language: Optional[str] = None,
0981a08… noreply 172 model: Optional[str] = None,
0981a08… noreply 173 ) -> dict:
0981a08… noreply 174 raise NotImplementedError(
0981a08… noreply 175 "AWS Bedrock does not support audio transcription directly. "
0981a08… noreply 176 "Use Amazon Transcribe or another provider for transcription."
0981a08… noreply 177 )
0981a08… noreply 178
0981a08… noreply 179 def list_models(self) -> list[ModelInfo]:
0981a08… noreply 180 return list(_BEDROCK_MODELS)
0981a08… noreply 181
0981a08… noreply 182
0981a08… noreply 183 ProviderRegistry.register(
0981a08… noreply 184 name="bedrock",
0981a08… noreply 185 provider_class=BedrockProvider,
0981a08… noreply 186 env_var="AWS_ACCESS_KEY_ID",
0981a08… noreply 187 model_prefixes=["bedrock/"],
0981a08… noreply 188 default_models={
0981a08… noreply 189 "chat": "anthropic.claude-3-sonnet-20240229-v1:0",
0981a08… noreply 190 "vision": "anthropic.claude-3-sonnet-20240229-v1:0",
0981a08… noreply 191 "audio": "",
0981a08… noreply 192 },
0981a08… noreply 193 )

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button