Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

-x/--extract option for returning content of first fenced code block #681

Closed
simonw opened this issue Dec 19, 2024 · 10 comments
Closed

-x/--extract option for returning content of first fenced code block #681

simonw opened this issue Dec 19, 2024 · 10 comments
Labels
enhancement New feature or request

Comments

@simonw
Copy link
Owner

simonw commented Dec 19, 2024

Thought of this after writing up my one-shot prompting tricks here: https://simonwillison.net/2024/Dec/19/one-shot-python-tools/

Idea is to be able to do this:

llm 'Python CLI tool for finding all files matching an expression, recursively' --extract

This would run the provided prompt and return just the content of the first fenced code block (```python ...) in the response.

If no fenced code blocks found, returns the whole string.

@simonw simonw added the enhancement New feature or request label Dec 19, 2024
@simonw
Copy link
Owner Author

simonw commented Dec 19, 2024

Used o1-mini for the initial code: https://chatgpt.com/share/67642b13-72a4-8006-8620-f75e62706554

@simonw simonw closed this as completed in 67d4a99 Dec 19, 2024
@simonw
Copy link
Owner Author

simonw commented Dec 19, 2024

@simonw
Copy link
Owner Author

simonw commented Dec 19, 2024

Demo:

llm -m gpt-4o-mini 'javascript function to reverse a string' -x

Output:

function reverseString(str) {
    return str.split('').reverse().join('');
}

// Example usage
const originalString = "Hello, World!";
const reversedString = reverseString(originalString);
console.log(reversedString); // Output: "!dlroW ,olleH"

But running llm logs -c shows the full prompt and response: https://gist.github.com/simonw/0964cdd9d7e3cb46932d9b017083f25a

@simonw
Copy link
Owner Author

simonw commented Dec 19, 2024

Templates should support this too.

@simonw simonw reopened this Dec 19, 2024
@simonw simonw closed this as completed in 000e984 Dec 19, 2024
@simonw
Copy link
Owner Author

simonw commented Dec 19, 2024

This now works:

llm --system 'write a Python function' --extract --save python-function
llm -t python-function 'reverse a string'

Docs here: https://github.com/simonw/llm/blob/000e984def983aa36384a24df42d4dbb558b5bb1/docs/templates.md#configuring-code-extraction

@simonw
Copy link
Owner Author

simonw commented Dec 19, 2024

From my blog entry: https://simonwillison.net/2024/Dec/19/one-shot-python-tools/

llm -s 'You write Python tools as single files. They always start with this comment:

# /// script
# requires-python = ">=3.12"
# ///

These files can include dependencies on libraries such as Click. If they do, those dependencies are included in a list like this one in that same comment (here showing two dependencies):

# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "click",
#     "sqlite-utils",
# ]
# ///' --save python --extract

@simonw
Copy link
Owner Author

simonw commented Dec 19, 2024

I tried this:

llm -t python 'starlette server providing /?url= API to extract just text using BeautifulSoup'

And got back this:

# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "starlette",
#     "httpx",
#     "beautifulsoup4",
# ]
# ///

from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.requests import Request
import httpx
from bs4 import BeautifulSoup

app = Starlette()

@app.route("/", methods=["GET"])
async def extract_text(request: Request):
    url = request.query_params.get("url")

    if not url:
        return JSONResponse({"error": "URL parameter is required"}, status_code=400)
    
    try:
        async with httpx.AsyncClient() as client:
            response = await client.get(url)
            response.raise_for_status()
    except httpx.RequestError:
        return JSONResponse({"error": "Failed to fetch the URL"}, status_code=500)
    except httpx.HTTPStatusError as exc:
        return JSONResponse({"error": f"HTTP error occurred: {exc.response.status_code}"}, status_code=exc.response.status_code)

    soup = BeautifulSoup(response.text, 'html.parser')
    text = soup.get_text()

    return JSONResponse({"text": text.strip()})

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=8000)

Full transcript here: https://gist.github.com/simonw/b32bb1b9ad3ea36247189f0d66cfc38e

That was with gpt-4o-mini.

@cmosguy
Copy link

cmosguy commented Jan 4, 2025

Hey @simonw how do we do this for o1-preview?

llm -m o1-preview-azure -t python-function 'reverse a string'                                                                                                   

Error: Error code: 400 - {'error': {'message': "Unsupported value: 'messages[0].role' does not support 'system' with this model.", 'type': 'invalid_request_error', 'param': 'messages[0].role', 'code': 'unsupported_value'}}

BTW, I had to do this:

llm -m o1-preview-azure 'You are an expert Python software engineer with over 20 years of experience.  create a script that will collect all the IBM LSF information using the necessary commands to tell me which host is available' -x

That seemed to work ok, but I cannot use templates yet with o1.

@simonw
Copy link
Owner Author

simonw commented Jan 23, 2025

That's because o1-preview doesn't support system messages at all. You can't use templates that set system messages with that model.

simonw added a commit that referenced this issue Jan 23, 2025
@cmosguy
Copy link

cmosguy commented Jan 24, 2025

@simonw Yes I suspected that after I wrote that last comment. Thanks for following up on this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants