# walk


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

A walk step shows a passage, then a short note from the tutor, then one
check. The journey (`journey.py`) plans the steps and picks the check of
each step. This module runs a step.

The note tells the learner what to look for in the passage. It does not
answer the check. The short-answer check is a normal tutor question,
with two changes: the step decides its way and depth, and each attempt
keeps the walk, the step, the section and the page. A sitting log counts
every check, also the checks that do not go through
[`Session.answer`](https://vedicreader.github.io/abhyasa/tutor.html#session.answer).

In a code task, the learner writes one function. The tests run in a
closed process with no network.

## The note

The prompt for the note. It gets the idea, the passage and the kind of
the next check. It never gets the check question. A step with no
passage, as in a walk from a topic, uses another prompt: the note
explains the idea itself, from its statement and the passages of your
sources that name it, and it never says that a passage is missing.

The reply is cut to 3 sentences and to `walks.note` words (60 by
default), even when the model writes more.

[`tutor_note`](https://vedicreader.github.io/abhyasa/walk.html#tutor_note)
writes the note once and keeps it on the step. A later call gives the
kept note. A note that says that no passage was given is a failed note:
the statement of the idea takes its place.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/abhyasa/blob/main/abhyasa/walk.py#L44"
target="_blank" style="float:right; font-size:smaller">source</a>

### tutor_note

``` python
def tutor_note(
    lr, ask, j, i:int, sources:NoneType=None
)->str:
```

*The note for step `i` of walk `j`: kept on the step, else written from
the idea and the passage, or from the idea and `sources` when the step
has no passage.*

## A short answer

Each attempt of a step keeps its place in the source.
[`step_meta`](https://vedicreader.github.io/abhyasa/walk.html#step_meta)
makes that record. In a side walk, `from` names the step that opened it,
as `<walk id>/<step>`.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/abhyasa/blob/main/abhyasa/walk.py#L62"
target="_blank" style="float:right; font-size:smaller">source</a>

### step_meta

``` python
def step_meta(
    j, i:int
)->dict:
```

*The walk, the step, the section and the page of step `i`, for the
record of each attempt. A side walk adds `from`, the step that opened
it.*

[`open_short`](https://vedicreader.github.io/abhyasa/walk.html#open_short)
opens the question of a step. The check of the step gives the way and
the depth. The note goes into the question prompt. The question id stays
on the step.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/abhyasa/blob/main/abhyasa/walk.py#L70"
target="_blank" style="float:right; font-size:smaller">source</a>

### open_short

``` python
def open_short(
    tutor:abhyasa.tutor.Tutor, j, i:int, session:str
)->fastcore.basics.AttrDict:
```

*Open the short-answer check of step `i` of walk `j`, and keep its id on
the step.*

## The sitting log

A demo or an order prediction does not go through
[`Session.answer`](https://vedicreader.github.io/abhyasa/tutor.html#session.answer).
`noted` adds one row for it, in the same shape as the rows of a
question.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/abhyasa/blob/main/abhyasa/walk.py#L81"
target="_blank" style="float:right; font-size:smaller">source</a>

### Session.noted

``` python
def noted(
    a:dict, floor0:int
):
```

*Add one log row for the attempt `a`, made outside
[`Session.answer`](https://vedicreader.github.io/abhyasa/tutor.html#session.answer)
(a demo or an order prediction), and save the sitting.*

## A code task

The model writes the exercise: a task, a stub, a function name and the
tests.
[`code_task`](https://vedicreader.github.io/abhyasa/walk.html#code_task)
keeps the full exercise on the tutor. The page gets the question, the
stub, the function name and the text of the visible tests. The solution
and the hidden tests stay on the server.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/abhyasa/blob/main/abhyasa/walk.py#L90"
target="_blank" style="float:right; font-size:smaller">source</a>

### code_task

``` python
def code_task(
    tutor:abhyasa.tutor.Tutor, c, session:str='', meta:dict=None
)->fastcore.basics.AttrDict:
```

*Open a code task on concept `c`. Return the public question, the stub,
the function name and the text of the visible tests.*

[`_first_error`](https://vedicreader.github.io/abhyasa/walk.html#_first_error)
gives the first failing test as text. A hidden test shows only its label
and its status. A visible test shows its text, the error and the value
that the function gave.

[`run_code`](https://vedicreader.github.io/abhyasa/walk.html#run_code)
grades one try. A syntax error uses no try: `compile` checks the code
and runs none of it. A failing try keeps the task open. A pass or the
last try closes it, as
[`Exercise.check`](https://vedicreader.github.io/abhyasa/nb.html#exercise.check)
does.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/abhyasa/blob/main/abhyasa/walk.py#L112"
target="_blank" style="float:right; font-size:smaller">source</a>

### run_code

``` python
def run_code(
    tutor:abhyasa.tutor.Tutor, qid:str, source:str, confidence:int=70
)->fastcore.basics.AttrDict:
```

*Run `source` on the tests of the code task `qid` in a closed process,
and grade it. A pass or the last try closes the task.*

[`Session.code`](https://vedicreader.github.io/abhyasa/walk.html#session.code)
runs a try on the open question of the sitting. A close goes in the
sitting log.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/abhyasa/blob/main/abhyasa/walk.py#L134"
target="_blank" style="float:right; font-size:smaller">source</a>

### Session.code

``` python
def code(
    source:str, confidence:int=70
)->fastcore.basics.AttrDict:
```

*Run `source` on the open code task of the sitting, log a close, and
save the sitting.*

## Tests for the step runtime

These tests use the small paper in `tests/walk_data.py` and a fake
model.

A walk with one fake model. It gives a note, a grade and a question.

``` python
lr = temp_learning(); did = add_paper(lr)
ask = Fake({'Return JSON: {"concepts"': EXTRACT_REPLY, 'You write a short note': 'Softmax makes weights from scores. Look at what a large score does.',
            'You grade one answer': dict(correct=True, grade=9, first_error='', misconception='', feedback='Yes.')},
           dflt=dict(question='Why do the weights add to 1?', answer='Each is divided by the sum.', rubric=['divide by the sum']))
j = journey_from_source(lr, ask, did)
t = Tutor(lr, ask, context=False)
```

The note is kept on its step. A second call makes no model call.

``` python
n = tutor_note(lr, ask, j, 1)
assert n.startswith('Softmax makes') and lr.journey(j.id).steps[1]['note'] == n
assert tutor_note(lr, Fake(), lr.journey(j.id), 1) == n
```

The note keeps at most 3 sentences and `walks.note` words (60 by
default).

``` python
long = tutor_note(lr, Fake({'You write a short note': 'One. Two. Three. Four.'}), lr.journey(j.id), 0)
assert long == 'One. Two. Three.'
wide = tutor_note(lr, Fake({'You write a short note': ' '.join(['word'] * 40) + '. ' + ' '.join(['more'] * 40) + '.'}), lr.journey(j.id), 2)
assert len(wide.split()) <= 60
```

A step with no passage, as in a walk from a topic, gets a note that
explains the idea itself, with the prompt `walk.note_topic`. The request
has the quoted name and statement of the idea. It does not ask for a
summary of a passage.

``` python
lr.walk_step(j.id, 0, read=None, note='')
ask2 = Fake({'You write a short note': 'Sequential computation runs one step after another. Look at the order of the steps.'})
n0 = tutor_note(lr, ask2, lr.journey(j.id), 0); p, sp = ask2.calls[-1]
assert n0.startswith('Sequential computation runs') and sp == prompt('walk.note_topic') and 'just read a passage' not in sp
assert 'The passage:' not in p and 'Idea: "Sequential computation"' in p and 'Statement: "A recurrent model computes' in p
```

With sources, the passages that name the idea go in the request as data,
between `<source>` markers.

``` python
class Notes:
    name = 'notes'
    def search(self, q, k=4): return [passage('vault:notes', 'n1', 'My notes', 'Sequential computation stops parallel work.')]
lr.walk_step(j.id, 0, note=''); tutor_note(lr, ask2, lr.journey(j.id), 0, sources=[Notes()])
assert '<source title="My notes">Sequential computation stops parallel work.</source>' in ask2.calls[-1][0]
```

A note that still says that no passage was given is a failed note. The
step gets the statement of the idea in its place.

``` python
for bad in ('No passage was provided, so there is nothing to summarize.', 'The text was not provided. Look for the order.'):
    lr.walk_step(j.id, 0, note='')
    got = tutor_note(lr, Fake({'You write a short note': bad}), lr.journey(j.id), 0)
    assert got == _trim(lr.concept('sequential-computation').statement) and lr.journey(j.id).steps[0]['note'] == got, got
```

The step decides the way and the depth. The question comes without its
answer, and the prompt has the note.

``` python
s = Session(t, journey=j.id)
lr.walk_step(j.id, 1, check=dict(way='words', kind='short', depth=2, why='plain words fit'))
q = open_short(t, lr.journey(j.id), 1, s.id); s.q = q
assert q.method == 'words' and q.depth == 2
assert 'answer' not in q and 'rubric' not in q and lr.journey(j.id).steps[1]['qid'] == q.id
assert 'The learner just read this' in ask.calls[-1][0] and n in ask.calls[-1][0]
```

The attempt has its walk, step, section and page. The sitting log has
the close.

``` python
r = s.answer('Each weight is divided by the sum of all.')
a = lr.attempts('softmax', limit=1)[0]
assert r.done and (a.walk, a.step, a.section, a.page) == (j.id, 1, '3.2.1 Scaled Dot-Product Attention', 4)
assert s.log[-1]['concept'] == 'softmax'
```

The review rule: the last way of a walk check is left out of the next
question on the idea.

``` python
lr.update('softmax', due=0)
assert all(t.ask_on(lr.concept('softmax'), 3).method != a.method for _ in range(10))
```

A demo or an order prediction is not made in
[`Session.answer`](https://vedicreader.github.io/abhyasa/tutor.html#session.answer).
`noted` adds its row to the sitting log and saves it.

``` python
s.noted(dict(concept='softmax', question='Predict', correct=True, outcome='solid', method='demo', kind='demo'), floor0=2)
assert s.log[-1]['method'] == 'demo' and lr.session(s.id).log[-1]['method'] == 'demo'
```

## Tests for a code task

A new walk and a sitting. The fake model writes the softmax exercise of
`tests/walk_data.py`.

``` python
lr = temp_learning(); did = add_paper(lr)
ask = Fake({'Return JSON: {"concepts"': EXTRACT_REPLY, 'You write one small Python exercise': EXERCISE, 'Teach the smallest step': 'Take away the max. Check: what is softmax([5, 5])?'})
j = journey_from_source(lr, ask, did); t = Tutor(lr, ask, context=False); s = Session(t, journey=j.id)
k = code_task(t, lr.concept('softmax'), s.id, step_meta(j, 1)); s.q = k.q
```

The stub has no part of the solution. The page gets the two visible
tests. The text of the hidden test stays on the server, and so do the
answer and the rubric.

``` python
assert k.fname == 'softmax' and 'm = max(x)' not in k.stub and len(k.tests) == 2
assert 'answer' not in k.q and 'rubric' not in k.q
```

A syntax error uses no try. The message gives the line. A `return`
outside a function and a null byte are syntax errors too, though
`ast.parse` accepts the first.

``` python
r = s.code('def softmax(x, t=1.0)\n    return x')
assert r.syntax.startswith('line 1') and r.tries_left == 3 and not r.done
for bad in ('return 1', 'x = 1\0'): assert s.code(bad).syntax.startswith('line 1')
assert t.open[s.q.id].tries == []
```

The naive softmax fails on large scores. Two of three tests pass, and
two tries are left. A hidden test shows no value.

``` python
r = s.code(NAIVE)
assert (r.passed, r.total, r.done, r.tries_left) == (2, 3, False, 2) and 'large scores' in r.first_error
assert not any(c['got'] for c in r.checks if c['hidden'])
```

A correct softmax closes the task. The attempt keeps the kind, the
depth, the way and the walk. The sitting log has the close, and the way
`code` gets the credit.

``` python
r = s.code(GOOD)
a = lr.attempts('softmax', limit=1)[0]
assert r.done and r.correct and (a.kind, a.depth, a.method, a.walk) == ('code', 4, 'code', j.id)
assert s.log[-1]['concept'] == 'softmax' and lr.stats().methods['*']['code'][0] > 0
```
