|
| 1 | +""" |
| 2 | +Banner display for Hindsight API startup. |
| 3 | +
|
| 4 | +Shows the logo and tagline with gradient colors. |
| 5 | +""" |
| 6 | + |
| 7 | +# Gradient colors: #0074d9 -> #009296 |
| 8 | +GRADIENT_START = (0, 116, 217) # #0074d9 |
| 9 | +GRADIENT_END = (0, 146, 150) # #009296 |
| 10 | + |
| 11 | +# Pre-generated logo (generated by test-logo.py) |
| 12 | +LOGO = """\ |
| 13 | + \033[38;2;9;127;184m\u2584\033[0m\033[48;2;8;130;178m\033[38;2;5;133;186m\u2584\033[0m \033[48;2;10;143;160m\033[38;2;10;143;165m\u2584\033[0m\033[38;2;7;140;156m\u2584\033[0m |
| 14 | + \033[38;2;8;125;192m\u2584\033[0m \033[38;2;3;132;191m\u2580\033[0m\033[38;2;2;133;192m\u2584\033[0m \033[38;2;3;132;180m\u2584\033[0m\033[38;2;1;137;184m\u2584\033[0m\033[38;2;3;133;174m\u2584\033[0m \033[38;2;3;142;176m\u2584\033[0m\033[38;2;4;142;169m\u2580\033[0m \033[38;2;10;144;164m\u2584\033[0m |
| 15 | +\033[38;2;6;121;195m\u2580\033[0m\033[38;2;5;128;203m\u2580\033[0m\033[48;2;5;124;195m\033[38;2;3;125;200m\u2584\033[0m\033[38;2;2;126;196m\u2584\033[0m\033[48;2;3;128;188m\033[38;2;1;131;196m\u2584\033[0m\033[48;2;0;152;219m\033[38;2;2;131;191m\u2584\033[0m\033[38;2;1;141;196m\u2580\033[0m\033[38;2;1;135;183m\u2580\033[0m\033[38;2;1;148;198m\u2580\033[0m\033[48;2;1;156;202m\033[38;2;2;135;180m\u2584\033[0m\033[48;2;4;134;169m\033[38;2;1;137;177m\u2584\033[0m\033[38;2;3;138;173m\u2584\033[0m\033[48;2;6;137;165m\033[38;2;2;140;170m\u2584\033[0m\033[38;2;7;144;169m\u2580\033[0m\033[38;2;7;139;158m\u2580\033[0m |
| 16 | + \033[48;2;2;128;202m\033[38;2;2;124;201m\u2584\033[0m\033[48;2;1;130;201m\033[38;2;0;135;212m\u2584\033[0m\033[38;2;2;128;196m\u2584\033[0m \033[48;2;2;142;204m\033[38;2;7;138;199m\u2584\033[0m \033[38;2;1;135;186m\u2584\033[0m\033[48;2;1;142;186m\033[38;2;2;144;194m\u2584\033[0m\033[48;2;3;138;176m\033[38;2;2;134;176m\u2584\033[0m |
| 17 | + \033[48;2;8;118;200m\033[38;2;8;121;209m\u2584\033[0m\033[38;2;3;121;203m\u2580\033[0m \033[38;2;3;122;192m\u2580\033[0m\033[38;2;1;138;216m\u2580\033[0m\033[48;2;0;138;210m\033[38;2;3;128;198m\u2584\033[0m\033[48;2;0;126;188m\033[38;2;2;131;198m\u2584\033[0m\033[48;2;0;142;205m\033[38;2;3;132;193m\u2584\033[0m\033[38;2;1;140;196m\u2580\033[0m \033[38;2;4;134;175m\u2580\033[0m\033[48;2;13;135;167m\033[38;2;8;136;174m\u2584\033[0m """ |
| 18 | + |
| 19 | + |
| 20 | +def _interpolate_color(start: tuple, end: tuple, t: float) -> tuple: |
| 21 | + """Interpolate between two RGB colors.""" |
| 22 | + return ( |
| 23 | + int(start[0] + (end[0] - start[0]) * t), |
| 24 | + int(start[1] + (end[1] - start[1]) * t), |
| 25 | + int(start[2] + (end[2] - start[2]) * t), |
| 26 | + ) |
| 27 | + |
| 28 | + |
| 29 | +def gradient_text(text: str, start: tuple = GRADIENT_START, end: tuple = GRADIENT_END) -> str: |
| 30 | + """Render text with a gradient color effect.""" |
| 31 | + result = [] |
| 32 | + length = len(text) |
| 33 | + for i, char in enumerate(text): |
| 34 | + if char == ' ': |
| 35 | + result.append(' ') |
| 36 | + else: |
| 37 | + t = i / max(length - 1, 1) |
| 38 | + r, g, b = _interpolate_color(start, end, t) |
| 39 | + result.append(f"\033[38;2;{r};{g};{b}m{char}") |
| 40 | + result.append("\033[0m") |
| 41 | + return "".join(result) |
| 42 | + |
| 43 | + |
| 44 | +def print_banner(): |
| 45 | + """Print the Hindsight startup banner.""" |
| 46 | + print(LOGO) |
| 47 | + tagline = gradient_text("Hindsight: Agent Memory That Works Like Human Memory") |
| 48 | + print(f"\n {tagline}\n") |
| 49 | + |
| 50 | + |
| 51 | +def color(text: str, t: float = 0.0) -> str: |
| 52 | + """Color text using gradient position (0.0 = start, 1.0 = end).""" |
| 53 | + r, g, b = _interpolate_color(GRADIENT_START, GRADIENT_END, t) |
| 54 | + return f"\033[38;2;{r};{g};{b}m{text}\033[0m" |
| 55 | + |
| 56 | + |
| 57 | +def color_start(text: str) -> str: |
| 58 | + """Color text with gradient start color (#0074d9).""" |
| 59 | + return color(text, 0.0) |
| 60 | + |
| 61 | + |
| 62 | +def color_end(text: str) -> str: |
| 63 | + """Color text with gradient end color (#009296).""" |
| 64 | + return color(text, 1.0) |
| 65 | + |
| 66 | + |
| 67 | +def color_mid(text: str) -> str: |
| 68 | + """Color text with gradient middle color.""" |
| 69 | + return color(text, 0.5) |
| 70 | + |
| 71 | + |
| 72 | +def dim(text: str) -> str: |
| 73 | + """Dim/gray text.""" |
| 74 | + return f"\033[38;2;128;128;128m{text}\033[0m" |
| 75 | + |
| 76 | + |
| 77 | +def print_startup_info(host: str, port: int, database_url: str, llm_provider: str, |
| 78 | + llm_model: str, embeddings_provider: str, reranker_provider: str, |
| 79 | + mcp_enabled: bool = False): |
| 80 | + """Print styled startup information.""" |
| 81 | + print(color_start("Starting Hindsight API...")) |
| 82 | + print(f" {dim('URL:')} {color(f'http://{host}:{port}', 0.2)}") |
| 83 | + print(f" {dim('Database:')} {color(database_url, 0.4)}") |
| 84 | + print(f" {dim('LLM:')} {color(f'{llm_provider} / {llm_model}', 0.6)}") |
| 85 | + print(f" {dim('Embeddings:')} {color(embeddings_provider, 0.8)}") |
| 86 | + print(f" {dim('Reranker:')} {color(reranker_provider, 1.0)}") |
| 87 | + if mcp_enabled: |
| 88 | + print(f" {dim('MCP:')} {color_end('enabled at /mcp')}") |
| 89 | + print() |
0 commit comments