init
This commit is contained in:
149
post_llama.py
Executable file
149
post_llama.py
Executable file
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Post a message with an attached file (contents embedded in JSON) to an
|
||||
OpenAI‑compatible API and echo the response.
|
||||
|
||||
Defaults:
|
||||
• Server: https://llama-cpp.reeselink.com/v1/chat/completions
|
||||
• Model : gpt-oss-120b
|
||||
• No API key required (but can be supplied via --api-key)
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Helper functions
|
||||
# ----------------------------------------------------------------------
|
||||
def load_file_text(path: Path) -> str:
|
||||
"""Read the whole file as UTF‑8 text (fallback to binary → base64 if needed)."""
|
||||
try:
|
||||
return path.read_text(encoding="utf-8")
|
||||
except UnicodeDecodeError:
|
||||
# For non‑text files we fall back to a base64 representation.
|
||||
import base64
|
||||
return base64.b64encode(path.read_bytes()).decode("ascii")
|
||||
|
||||
|
||||
def build_payload(
|
||||
model: str,
|
||||
user_message: str,
|
||||
file_content: str,
|
||||
file_name: str,
|
||||
) -> dict:
|
||||
"""
|
||||
Construct the JSON body expected by the OpenAI chat completions endpoint.
|
||||
|
||||
The “file attachment” is modeled as an extra user‑role message that contains
|
||||
the file name and its contents. This mirrors the way many front‑ends embed
|
||||
files in the conversation history.
|
||||
"""
|
||||
# Primary user message (the prompt you want the model to answer)
|
||||
messages = [{"role": "user", "content": user_message}]
|
||||
|
||||
# Add a second message that represents the attached file.
|
||||
# The format is arbitrary – you can change it to suit your downstream model.
|
||||
file_message = (
|
||||
f"[Attached file: {file_name}]\n"
|
||||
f"{file_content}"
|
||||
)
|
||||
messages.append({"role": "user", "content": file_message})
|
||||
|
||||
return {
|
||||
"model": model,
|
||||
"messages": messages,
|
||||
# Optional parameters – feel free to expose them as CLI args later.
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 1024,
|
||||
}
|
||||
|
||||
|
||||
def post_chat_completion(
|
||||
endpoint: str,
|
||||
payload: dict,
|
||||
api_key: str | None = None,
|
||||
) -> requests.Response:
|
||||
"""POST the JSON payload to the API and return the raw response."""
|
||||
headers = {"Content-Type": "application/json"}
|
||||
if api_key:
|
||||
headers["Authorization"] = f"Bearer {api_key}"
|
||||
|
||||
response = requests.post(endpoint, headers=headers, json=payload, timeout=300)
|
||||
response.raise_for_status() # raise an exception for HTTP errors
|
||||
return response
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# CLI entry point
|
||||
# ----------------------------------------------------------------------
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Send a message + file contents to an OpenAI‑compatible API."
|
||||
)
|
||||
parser.add_argument(
|
||||
"file",
|
||||
type=Path,
|
||||
help="Path to the file whose contents will be sent in the payload.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
"--message",
|
||||
default="Please analyse the attached file.",
|
||||
help="User message to send alongside the file.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--model",
|
||||
default="gpt-oss-120b",
|
||||
help="Model name to request (default: %(default)s).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--api-key",
|
||||
default=None,
|
||||
help="Optional API key; omit if the server does not require authentication.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--url",
|
||||
default="https://llama-cpp.reeselink.com/v1/chat/completions",
|
||||
help="Base URL of the OpenAI‑compatible endpoint (default: %(default)s).",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Read file and build request
|
||||
# ------------------------------------------------------------------
|
||||
if not args.file.is_file():
|
||||
sys.exit(f"Error: '{args.file}' does not exist or is not a regular file.")
|
||||
|
||||
file_content = load_file_text(args.file)
|
||||
payload = build_payload(
|
||||
model=args.model,
|
||||
user_message=args.message,
|
||||
file_content=file_content,
|
||||
file_name=args.file.name,
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Send request
|
||||
# ------------------------------------------------------------------
|
||||
try:
|
||||
resp = post_chat_completion(endpoint=args.url, payload=payload, api_key=args.api_key)
|
||||
except requests.RequestException as exc:
|
||||
sys.exit(f"Request failed: {exc}")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Echo the response (pretty‑print JSON if possible)
|
||||
# ------------------------------------------------------------------
|
||||
try:
|
||||
json_resp = resp.json()
|
||||
print(json.dumps(json_resp["choices"][0]["message"]["content"], indent=2, ensure_ascii=False))
|
||||
except ValueError:
|
||||
# Not JSON – just dump raw text
|
||||
print(resp.text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user