Created
June 17, 2025 16:11
-
-
Save dhruvinsh/5dc5162e5acf6c863b3a9cafd10788af to your computer and use it in GitHub Desktop.
simonw/llm + python rich
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env -S uv run --script | |
| # /// script | |
| # requires-python = ">=3.12" | |
| # dependencies = ["rich", "aiofiles"] | |
| # /// | |
| # version 3: aiofiles | |
| import asyncio | |
| import sys | |
| import os | |
| from rich.console import Console, ConsoleOptions, RenderResult | |
| from rich.live import Live | |
| from rich.markdown import Markdown, CodeBlock | |
| from rich.syntax import Syntax | |
| from rich.text import Text | |
| import aiofiles | |
| async def read_stdin_async(): | |
| """Async generator that yields chunks from stdin""" | |
| loop = asyncio.get_event_loop() | |
| reader = asyncio.StreamReader() | |
| protocol = asyncio.StreamReaderProtocol(reader) | |
| await loop.connect_read_pipe(lambda: protocol, sys.stdin) | |
| while True: | |
| chunk = await reader.read(1024) | |
| if not chunk: | |
| break | |
| yield chunk.decode('utf-8', errors='replace') | |
| async def main(): | |
| prettier_code_blocks() | |
| console = Console() | |
| # Check if stdin has data | |
| if sys.stdin.isatty(): | |
| console.print("[red]No input received from stdin.[/red]") | |
| console.print("[dim]Example: echo '# Hello World' | ./stream[/dim]") | |
| return | |
| buffer = "" | |
| with Live("", console=console, vertical_overflow="visible", refresh_per_second=10) as live: | |
| try: | |
| async for chunk in read_stdin_async(): | |
| buffer += chunk | |
| live.update(Markdown(buffer)) | |
| except KeyboardInterrupt: | |
| console.print("\n[yellow]Interrupted by user[/yellow]") | |
| except Exception as e: | |
| console.print(f"\n[red]Error: {e}[/red]") | |
| finally: | |
| # Ensure final buffer is displayed | |
| if buffer and not live.is_started: | |
| console.print(Markdown(buffer)) | |
| def prettier_code_blocks(): | |
| """Make rich code blocks prettier and easier to copy. | |
| From https://github.com/samuelcolvin/aicli/blob/v0.8.0/samuelcolvin_aicli.py#L22 | |
| """ | |
| class SimpleCodeBlock(CodeBlock): | |
| def __rich_console__( | |
| self, console: Console, options: ConsoleOptions | |
| ) -> RenderResult: | |
| code = str(self.text).rstrip() | |
| yield Text(self.lexer_name, style='dim') | |
| yield Syntax( | |
| code, | |
| self.lexer_name, | |
| theme=self.theme, | |
| background_color='default', | |
| word_wrap=True, | |
| ) | |
| yield Text(f'/{self.lexer_name}', style='dim') | |
| Markdown.elements['fence'] = SimpleCodeBlock | |
| if __name__ == '__main__': | |
| try: | |
| asyncio.run(main()) | |
| except KeyboardInterrupt: | |
| pass | |
| # vim: set ft=py: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env -S uv run --script | |
| # /// script | |
| # requires-python = ">=3.12" | |
| # dependencies = ["rich"] | |
| # /// | |
| # version 1: inspired from pydantic-ai, fake simulated stream | |
| import asyncio | |
| import sys | |
| from rich.console import Console, ConsoleOptions, RenderResult | |
| from rich.live import Live | |
| from rich.markdown import Markdown, CodeBlock | |
| from rich.syntax import Syntax | |
| from rich.text import Text | |
| async def main(): | |
| prettier_code_blocks() | |
| console = Console() | |
| # Read from stdin | |
| console.print("\nResponse:", style='cyan') | |
| # Check if stdin is a pipe/has data | |
| if not sys.stdin.isatty(): | |
| # Read all input from stdin | |
| input_text = sys.stdin.read() | |
| # Display the input using Rich's Live display with Markdown rendering | |
| with Live('', console=console, vertical_overflow='visible') as live: | |
| # If you want to simulate streaming, you can do it character by character | |
| # or line by line. Here's a character-by-character approach: | |
| accumulated_text = "" | |
| for char in input_text: | |
| accumulated_text += char | |
| live.update(Markdown(accumulated_text)) | |
| await asyncio.sleep(0.001) # Small delay to simulate streaming | |
| # Alternatively, if you just want to display it all at once: | |
| # console.print(Markdown(input_text)) | |
| else: | |
| console.log("No input received from stdin. Pipe some text to this script.", style='red') | |
| console.log("Example: echo '# Hello World' | python script.py", style='dim') | |
| def prettier_code_blocks(): | |
| """Make rich code blocks prettier and easier to copy. | |
| From https://github.com/samuelcolvin/aicli/blob/v0.8.0/samuelcolvin_aicli.py#L22 | |
| """ | |
| class SimpleCodeBlock(CodeBlock): | |
| def __rich_console__( | |
| self, console: Console, options: ConsoleOptions | |
| ) -> RenderResult: | |
| code = str(self.text).rstrip() | |
| yield Text(self.lexer_name, style='dim') | |
| yield Syntax( | |
| code, | |
| self.lexer_name, | |
| theme=self.theme, | |
| background_color='default', | |
| word_wrap=True, | |
| ) | |
| yield Text(f'/{self.lexer_name}', style='dim') | |
| Markdown.elements['fence'] = SimpleCodeBlock | |
| if __name__ == '__main__': | |
| asyncio.run(main()) | |
| # vim: set ft=py: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env -S uv run --script | |
| # /// script | |
| # requires-python = ">=3.12" | |
| # dependencies = ["rich"] | |
| # /// | |
| # version 2: chunk size to control stream | |
| import asyncio | |
| import sys | |
| import select | |
| from rich.console import Console, ConsoleOptions, RenderResult | |
| from rich.live import Live | |
| from rich.markdown import Markdown, CodeBlock | |
| from rich.syntax import Syntax | |
| from rich.text import Text | |
| async def main(): | |
| prettier_code_blocks() | |
| console = Console() | |
| # Check if stdin has data | |
| if sys.stdin.isatty(): | |
| console.print("[red]No input received from stdin.[/red]") | |
| console.print("[dim]Example: echo '# Hello World' | ./stream[/dim]") | |
| return | |
| buffer = "" | |
| chunk_size = 30 # Read in larger chunks for better performance | |
| with Live("", console=console, vertical_overflow="visible", refresh_per_second=10) as live: | |
| try: | |
| while True: | |
| # Check if data is available to read | |
| if sys.stdin in select.select([sys.stdin], [], [], 0)[0]: | |
| # Read available data (non-blocking) | |
| chunk = sys.stdin.read(chunk_size) | |
| if not chunk: # EOF | |
| break | |
| buffer += chunk | |
| live.update(Markdown(buffer)) | |
| else: | |
| # Small delay to prevent busy waiting | |
| await asyncio.sleep(0.01) | |
| except KeyboardInterrupt: | |
| console.print("\n[yellow]Interrupted by user[/yellow]") | |
| except Exception as e: | |
| console.print(f"\n[red]Error: {e}[/red]") | |
| finally: | |
| # Ensure final buffer is displayed | |
| if buffer: | |
| console.print(Markdown(buffer)) | |
| def prettier_code_blocks(): | |
| """Make rich code blocks prettier and easier to copy. | |
| From https://github.com/samuelcolvin/aicli/blob/v0.8.0/samuelcolvin_aicli.py#L22 | |
| """ | |
| class SimpleCodeBlock(CodeBlock): | |
| def __rich_console__( | |
| self, console: Console, options: ConsoleOptions | |
| ) -> RenderResult: | |
| code = str(self.text).rstrip() | |
| yield Text(self.lexer_name, style='dim') | |
| yield Syntax( | |
| code, | |
| self.lexer_name, | |
| theme=self.theme, | |
| background_color='default', | |
| word_wrap=True, | |
| ) | |
| yield Text(f'/{self.lexer_name}', style='dim') | |
| Markdown.elements['fence'] = SimpleCodeBlock | |
| if __name__ == '__main__': | |
| try: | |
| asyncio.run(main()) | |
| except KeyboardInterrupt: | |
| pass | |
| # vim: set ft=py: |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment