Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ The [`Dialog`](https://AnswerDotAI.github.io/aidialog/dialog.html#dialog) is the
| canonical chat ([`Msg`](https://AnswerDotAI.github.io/aidialog/msg_parts.html#msg)/[`Part`](https://AnswerDotAI.github.io/aidialog/msg_parts.html#part)) | transmission, normalizing | [`chat2dlg`](https://AnswerDotAI.github.io/aidialog/hist.html#chat2dlg) | [`dlg2chat`](https://AnswerDotAI.github.io/aidialog/hist.html#dlg2chat) |
| hist (live call input) | transmission, one-way | | [`dlg2hist`](https://AnswerDotAI.github.io/aidialog/hist.html#dlg2hist) |
| a prompt’s reply | self-similar | [`reply2dlg`](https://AnswerDotAI.github.io/aidialog/hist.html#reply2dlg) | [`dlg2reply`](https://AnswerDotAI.github.io/aidialog/hist.html#dlg2reply) |
| XML views | display, one-way | | [`view_dlg`](https://AnswerDotAI.github.io/aidialog/dlgskill.html#view_dlg), [`msg2xml`](https://AnswerDotAI.github.io/aidialog/dlgskill.html#msg2xml) |
| XML views | display, one-way | | [`view_dlg`](https://AnswerDotAI.github.io/aidialog/dlgskill.html#view_dlg), [`msg2xml`](https://AnswerDotAI.github.io/aidialog/dialog.html#msg2xml) |

The session codecs (in [llmsurgery](https://github.com/AnswerDotAI/llmsurgery)) route through chat on their way to the wire: ant’s `dlg2msgs` and oai’s `dlg2items` are each `denorm_msgs(dlg2chat(...))`.

Expand Down Expand Up @@ -67,11 +67,28 @@ A quick taste - create a dialog, add a message, and view it as concise XML:
``` python
from aidialog.dlgskill import *
import tempfile
```

``` python
p = tempfile.mkdtemp() + '/demo.ipynb'
create_dlg(p, '## A tiny dialog', 'note')
add_msg('6*7', after=find_msgs(dlg=p)[0].id, dlg=p)
d = create_dlg(p, '## A tiny dialog', 'note')
add_msg('6*7', after=d.messages[0].id, dlg=p)
view_dlg(p)
```

<dialog name="demo"><markdown id="470ae519">## A tiny dialog</markdown><code id="c836ce0c">6*7</code></dialog>
<dialog name="demo"><markdown id="1d693c32">## A tiny dialog</markdown><code id="02048a43">6*7</code></dialog>

## Command line

The flat commands expose dialog-aware reading and structural edits without a Python kernel:

``` bash
aidialog-summary nbs/00_core.ipynb
aidialog-find nbs/00_core.ipynb 'read_csv' --context 1
aidialog-view nbs/00_core.ipynb ab12cd34 --out
aidialog-add nbs/00_core.ipynb --after ab12cd34 --msg-type code < new-cell.py
aidialog-del nbs/00_core.ipynb ab12cd34
aidialog-move nbs/00_core.ipynb ab12cd34,ef56ab78 --before 9012cdef
```

Message ID arguments are comma-separated where a command accepts several. Mutating commands accept `--dry-run`; run any command with `--help` for its full filters and display options.
3 changes: 2 additions & 1 deletion aidialog/_modidx.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
'doc_host': 'https://AnswerDotAI.github.io',
'git_url': 'https://github.com/AnswerDotAI/aidialog',
'lib_path': 'aidialog'},
'syms': { 'aidialog.dialog': { 'aidialog.dialog.Attachment': ('dialog.html#attachment', 'aidialog/dialog.py'),
'syms': { 'aidialog.cli': {},
'aidialog.dialog': { 'aidialog.dialog.Attachment': ('dialog.html#attachment', 'aidialog/dialog.py'),
'aidialog.dialog.Attachment.__init__': ('dialog.html#attachment.__init__', 'aidialog/dialog.py'),
'aidialog.dialog.Dialog': ('dialog.html#dialog', 'aidialog/dialog.py'),
'aidialog.dialog.Dialog.__bool__': ('dialog.html#dialog.__bool__', 'aidialog/dialog.py'),
Expand Down
125 changes: 125 additions & 0 deletions aidialog/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"Command-line access to dialog and notebook navigation and structure."

import sys
from fastcore.script import call_parse
from .dlgskill import summary_dlg, find_msgs, view_dlg, view_msgs
from .ipynb import read_ipynb


def _die(msg): raise SystemExit(msg)


def _dialog(fname):
if (res := read_ipynb(fname)) is None: _die(f"Could not read dialog: {fname}")
return res


def _placement(before, after, required=False):
if before and after: _die("Pass only one of --before and --after")
if required and not (before or after): _die("Pass one of --before and --after")


def _ids(ids): return [o.strip() for o in ids.split(',') if o.strip()]


@call_parse
def summary_cli(
fname:str, # Dialog or notebook path
maxlen:int=180, # Maximum characters displayed per message
):
"Show one preview row per message."
print(summary_dlg(fname, maxlen=maxlen))


@call_parse(pos=['pattern'])
def find_cli(
fname:str, # Dialog or notebook path
pattern:str='', # Regex or plain text to find
msg_type:str=None, # Limit matches to code, note, prompt, or raw messages
errors:bool=False, # Match only code messages with errors?
exported:bool=False, # Match only exported messages?
ids:str='', # Comma-separated message IDs to select
before:int=0, # Messages of context before each match
after:int=0, # Messages of context after each match
context:int=None, # Messages of context before and after each match
limit:int=None, # Maximum matched messages
case:bool=False, # Match case sensitively?
plain:bool=False, # Treat pattern as plain text rather than regex?
headers:bool=False, # Match only heading notes?
section:str=None, # Return the section beginning at this heading
):
"Find messages using dialog-aware filters and context."
print(find_msgs(pattern, dlg=fname, msg_type=msg_type, only_err=errors, only_exp=exported, ids=ids,
before=before, after=after, context=context, limit=limit, use_case=case, use_regex=not plain,
headers_only=headers, header_section=section))


@call_parse(pos=['ids'])
def view_cli(
fname:str, # Dialog or notebook path
ids:str='', # Comma-separated message IDs; omit to view the whole dialog
nums:bool=True, # Show line numbers for individual messages?
lnhashs:bool=False, # Show hash-verified line addresses instead of line numbers?
start_line:int=1, # First source line to show
end_line:int=None, # Last source line to show
out:bool=False, # Include prompt replies and code outputs?
full_out:bool=False, # Do not truncate included outputs?
errors:bool=False, # Show only code messages with errors?
):
"View complete dialogs or selected messages."
if (sel := _ids(ids)):
if errors: _die("--errors applies only when viewing a whole dialog")
res = view_msgs(*sel, dlg=fname, nums=nums, start_line=start_line, end_line=end_line,
lnhashs=lnhashs, incl_out=out, trunc_out=not full_out)
else:
if lnhashs or not nums or start_line != 1 or end_line is not None: _die("Line options require message IDs")
res = view_dlg(fname, incl_out=out, only_errors=errors, trunc_out=not full_out)
print(res)


@call_parse
def add_cli(
fname:str, # Dialog or notebook path
source:str=None, # Message source; defaults to stdin
msg_type:str='code', # Message type: code, note, prompt, or raw
before:str=None, # Insert before this message ID
after:str=None, # Insert after this message ID
export:bool=False, # Mark the new message for nbdev export?
dry_run:bool=False, # Preview without saving?
):
"Add a message, reading multiline source from stdin by default."
_placement(before, after)
if source is None: source = sys.stdin.read()
d = _dialog(fname)
m = d.mk_message(source, before=before, after=after, msg_type=msg_type, export=export)
if not dry_run: d.save()
print(m.preview())


@call_parse
def del_cli(
fname:str, # Dialog or notebook path
ids:str, # Comma-separated message IDs to delete
dry_run:bool=False, # Preview without saving?
):
"Delete messages by stable ID."
d = _dialog(fname)
removed = d.remove_msgs([d.msg(i) for i in _ids(ids)])
if not dry_run: d.save()
print('\n'.join(str(m.preview()) for m in removed))


@call_parse
def move_cli(
fname:str, # Dialog or notebook path
ids:str, # Comma-separated message IDs to move
before:str=None, # Move before this message ID
after:str=None, # Move after this message ID
dry_run:bool=False, # Preview without saving?
):
"Move messages while retaining their relative order."
_placement(before, after, required=True)
d = _dialog(fname)
d.move_msgs(_ids(ids), before=before, after=after)
if not dry_run: d.save()
print(d.summary())
46 changes: 32 additions & 14 deletions nbs/index.ipynb
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "f7806d71",
"metadata": {},
"outputs": [],
"source": [
"#| hide\n",
"from aidialog.dlgskill import *"
]
},
{
"cell_type": "markdown",
"id": "7b8db7d9",
Expand Down Expand Up @@ -162,6 +151,17 @@
"A quick taste - create a dialog, add a message, and view it as concise XML:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f7806d71",
"metadata": {},
"outputs": [],
"source": [
"from aidialog.dlgskill import *\n",
"import tempfile"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -180,14 +180,32 @@
}
],
"source": [
"from aidialog.dlgskill import *\n",
"import tempfile\n",
"\n",
"p = tempfile.mkdtemp() + '/demo.ipynb'\n",
"d = create_dlg(p, '## A tiny dialog', 'note')\n",
"add_msg('6*7', after=d.messages[0].id, dlg=p)\n",
"view_dlg(p)"
]
},
{
"cell_type": "markdown",
"id": "dfad191c",
"metadata": {},
"source": [
"## Command line\n",
"\n",
"The flat commands expose dialog-aware reading and structural edits without a Python kernel:\n",
"\n",
"```bash\n",
"aidialog-summary nbs/00_core.ipynb\n",
"aidialog-find nbs/00_core.ipynb 'read_csv' --context 1\n",
"aidialog-view nbs/00_core.ipynb ab12cd34 --out\n",
"aidialog-add nbs/00_core.ipynb --after ab12cd34 --msg-type code < new-cell.py\n",
"aidialog-del nbs/00_core.ipynb ab12cd34\n",
"aidialog-move nbs/00_core.ipynb ab12cd34,ef56ab78 --before 9012cdef\n",
"```\n",
"\n",
"Message ID arguments are comma-separated where a command accepts several. Mutating commands accept `--dry-run`; run any command with `--help` for its full filters and display options."
]
}
],
"metadata": {
Expand Down
8 changes: 8 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ aidialog = "aidialog._modidx:d"
[project.entry-points.pyskills]
"aidialog.dlgskill" = "aidialog.dlgskill"

[project.scripts]
aidialog-summary = "aidialog.cli:summary_cli"
aidialog-find = "aidialog.cli:find_cli"
aidialog-view = "aidialog.cli:view_cli"
aidialog-add = "aidialog.cli:add_cli"
aidialog-del = "aidialog.cli:del_cli"
aidialog-move = "aidialog.cli:move_cli"

[tool.setuptools.dynamic]
version = {attr = "aidialog.__version__"}

Expand Down